PerceptionCollectable.java

package org.opentrafficsim.road.gtu.perception;

import java.util.Iterator;
import java.util.function.Function;
import java.util.function.Supplier;

import org.djunits.value.vdouble.scalar.Length;
import org.opentrafficsim.base.DistancedObject;
import org.opentrafficsim.road.gtu.perception.object.PerceivedObject;

/**
 * Iterable that additionally provides support for PerceptionCollectors. These gather raw data, to only 'perceive' the result.
 * <p>
 * Copyright (c) 2013-2026 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 Alexander Verbraeck
 * @author Peter Knoppers
 * @author Wouter Schakel
 * @param <P> perceived object type
 * @param <U> underlying object type
 */
public interface PerceptionCollectable<P extends PerceivedObject, U> extends PerceptionIterable<P>
{

    /**
     * Collect the underlying objects in to a perceived result. This methodology is loosely based on Stream.collect().
     * @param collector collector
     * @param <C> collection result type
     * @param <I> intermediate type
     * @return collection result
     */
    default <C, I> C collect(final PerceptionCollector<C, ? super U, I> collector)
    {
        return collect(collector.getIdentity(), collector.getAccumulator(), collector.getFinalizer());
    }

    /**
     * Collect the underlying objects in to a perceived result. This methodology is loosely based on Stream.collect().
     * @param identity the initial intermediate result value
     * @param accumulator accumulator
     * @param finalizer finalizer
     * @param <C> collection result type
     * @param <I> intermediate type
     * @return collection result
     */
    <C, I> C collect(Supplier<I> identity, PerceptionAccumulator<? super U, I> accumulator, Function<I, C> finalizer);

    /**
     * Returns an iterator over the underlying objects.
     * @return iterator
     */
    Iterator<U> underlying();

    /**
     * Returns an iterator over the underlying objects coupled with the distance.
     * @return iterator
     */
    Iterator<DistancedObject<U>> underlyingWithDistance();

    /**
     * Combination of an accumulator and a finalizer.
     * @param <C> collection result type
     * @param <U> underlying object type
     * @param <I> intermediate result type
     */
    interface PerceptionCollector<C, U, I>
    {
        /**
         * Returns the identity value, the initial intermediate value.
         * @return identity value, the initial intermediate value
         */
        Supplier<I> getIdentity();

        /**
         * Returns the accumulator.
         * @return accumulator
         */
        PerceptionAccumulator<U, I> getAccumulator();

        /**
         * Returns the finalizer.
         * @return finalizer
         */
        Function<I, C> getFinalizer();
    }

    /**
     * Accumulates an object one at a time in to an accumulating intermediate result.
     * @param <U> underlying object type
     * @param <I> intermediate result type
     */
    @FunctionalInterface
    interface PerceptionAccumulator<U, I>
    {
        /**
         * Accumulate the next object to intermediate result.
         * @param intermediate intermediate result before accumulation of object
         * @param object next object to include
         * @param distance distance to the considered object
         * @return intermediate result after accumulation of object
         */
        Intermediate<I> accumulate(Intermediate<I> intermediate, U object, Length distance);
    }

    /**
     * Wrapper of intermediate result with info for the iterator algorithm.
     * @param <I> intermediate result type
     */
    class Intermediate<I>
    {
        /** Number of underlying objects being iterated. */
        private int number = 0;

        /** Intermediate object. */
        private I object;

        /** Whether to stop accumulating. */
        private boolean stop = false;

        /**
         * Constructor.
         * @param object identity value
         */
        public Intermediate(final I object)
        {
            this.object = object;
        }

        /**
         * Get intermediate object.
         * @return intermediate object
         */
        public I getObject()
        {
            return this.object;
        }

        /**
         * Set intermediate object.
         * @param object intermediate object
         */
        public void setObject(final I object)
        {
            this.object = object;
        }

        /**
         * Returns the number of the underlying object currently being accumulated, starts at 0 for the first.
         * @return number of the underlying object currently being accumulated
         */
        public int getNumber()
        {
            return this.number;
        }

        /**
         * Method for the iterator to increase the underlying object number.
         */
        public void step()
        {
            this.number++;
        }

        /**
         * Method for the accumulator to indicate the iterator can stop.
         */
        public void stop()
        {
            this.stop = true;
        }

        /**
         * Method for the iterator to check if it can stop.
         * @return whether the iterator can stop
         */
        public boolean isStop()
        {
            return this.stop;
        }
    }

}