AbstractPerceptionReiterable.java
package org.opentrafficsim.road.gtu.lane.perception;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Supplier;
import org.djunits.value.vdouble.scalar.Length;
import org.opentrafficsim.base.parameters.ParameterException;
import org.opentrafficsim.core.gtu.GtuException;
import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
/**
* This class uses a single primary iterator which a subclass defines, and makes sure that all elements are only looked up and
* created once. It does so by storing the elements in a linked list. All calls to {@code iterator()} return an iterator which
* iterates over the linked list. If an iterator runs to the end of the linked list, the primary iterator is requested to add an
* element if it has one.
* <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 <H> headway type
* @param <U> underlying object type
*/
public abstract class AbstractPerceptionReiterable<H extends Headway, U> implements PerceptionCollectable<H, U>
{
/** First entry. */
private SecondaryIteratorEntry first;
/** Last entry generated by the primary iterator. */
private SecondaryIteratorEntry last;
/** Primary iterator. */
private Iterator<PrimaryIteratorEntry> primaryIterator;
/** Perceiving GTU. */
private final LaneBasedGtu gtu;
/**
* Constructor.
* @param perceivingGtu LaneBasedGtu; perceiving GTU
*/
protected AbstractPerceptionReiterable(final LaneBasedGtu perceivingGtu)
{
this.gtu = perceivingGtu;
}
/**
* Returns the GTU.
* @return LaneBasedGtu; GTU
*/
protected LaneBasedGtu getGtu()
{
return this.gtu;
}
/**
* Returns the primary iterator.
* @return Iterator; primary iterator
*/
final Iterator<PrimaryIteratorEntry> getPrimaryIterator()
{
if (this.primaryIterator == null)
{
this.primaryIterator = primaryIterator();
}
return this.primaryIterator;
}
/**
* Returns the primary iterator. This method is called once by AbstractPerceptionReiterable.
* @return Iterator; primary iterator
*/
protected abstract Iterator<PrimaryIteratorEntry> primaryIterator();
/**
* Returns a perceived version of the underlying object.
* @param perceivingGtu LaneBasedGtu; perceiving GTU
* @param object U; underlying object
* @param distance Length; distance to the object
* @return H; perceived version of the underlying object
* @throws GtuException on exception
* @throws ParameterException on invalid parameter value or missing parameter
*/
protected abstract H perceive(LaneBasedGtu perceivingGtu, U object, Length distance)
throws GtuException, ParameterException;
/** {@inheritDoc} */
@Override
public final synchronized H first()
{
assureFirst();
if (this.first == null)
{
return null;
}
return this.first.getValue();
}
/**
* Assures a first SecondaryIteratorEntry is present, if the primary iterator has any elements.
*/
private synchronized void assureFirst()
{
if (this.first == null && getPrimaryIterator().hasNext())
{
addNext(getPrimaryIterator().next());
}
}
/**
* Adds an iterator entry to the internal linked list.
* @param next PrimaryIteratorEntry; next object
*/
@SuppressWarnings("synthetic-access")
final void addNext(final PrimaryIteratorEntry next)
{
SecondaryIteratorEntry entry = new SecondaryIteratorEntry(next.object, next.distance);
if (AbstractPerceptionReiterable.this.last == null)
{
AbstractPerceptionReiterable.this.first = entry;
AbstractPerceptionReiterable.this.last = entry;
}
else
{
AbstractPerceptionReiterable.this.last.next = entry;
AbstractPerceptionReiterable.this.last = entry;
}
}
/** {@inheritDoc} */
@Override
public final boolean isEmpty()
{
return first() == null;
}
/** {@inheritDoc} */
@Override
public final Iterator<H> iterator()
{
return new PerceptionIterator();
}
/** {@inheritDoc} */
@Override
public final <C, I> C collect(final Supplier<I> identity, final PerceptionAccumulator<? super U, I> accumulator,
final PerceptionFinalizer<C, I> finalizer)
{
Intermediate<I> intermediate = new Intermediate<>(identity.get());
assureFirst();
if (this.first != null)
{
SecondaryIteratorEntry lastReturned = null;
SecondaryIteratorEntry next = this.first;
next = assureNext(next, lastReturned);
while (next != null && !intermediate.isStop())
{
intermediate = accumulator.accumulate(intermediate, next.object, next.distance);
intermediate.step();
lastReturned = next;
next = lastReturned.next;
next = assureNext(next, lastReturned);
}
}
return finalizer.collect(intermediate.getObject());
}
/** {@inheritDoc} */
@Override
public Iterator<U> underlying()
{
assureFirst();
SecondaryIteratorEntry firstInContext = this.first;
return new Iterator<U>()
{
/** Last returned iterator entry. */
private SecondaryIteratorEntry lastReturned = null;
/** Next iterator entry. */
private SecondaryIteratorEntry next = firstInContext;
/** {@inheritDoc} */
@Override
public boolean hasNext()
{
this.next = assureNext(this.next, this.lastReturned);
return this.next != null;
}
/** {@inheritDoc} */
@Override
public U next()
{
// this.next = assureNext(this.next, this.lastReturned);
// if (this.next == null)
// {
// throw new NoSuchElementException();
// }
// this.lastReturned = this.next;
// this.next = this.lastReturned.next;
// return this.lastReturned.object;
this.lastReturned = this.next;
this.next = this.lastReturned.next;
this.next = assureNext(this.next, this.lastReturned);
return this.lastReturned.object;
}
};
}
/** {@inheritDoc} */
@Override
public Iterator<UnderlyingDistance<U>> underlyingWithDistance()
{
assureFirst();
SecondaryIteratorEntry firstInContext = this.first;
return new Iterator<UnderlyingDistance<U>>()
{
/** Last returned iterator entry. */
private SecondaryIteratorEntry lastReturned = null;
/** Next iterator entry. */
private SecondaryIteratorEntry next = firstInContext;
/** {@inheritDoc} */
@Override
public boolean hasNext()
{
this.next = assureNext(this.next, this.lastReturned);
return this.next != null;
}
/** {@inheritDoc} */
@Override
public UnderlyingDistance<U> next()
{
this.lastReturned = this.next;
this.next = this.lastReturned.next;
this.next = assureNext(this.next, this.lastReturned);
return new UnderlyingDistance<>(this.lastReturned.object, this.lastReturned.distance);
}
};
}
/**
* This iterator is returned to callers of the {@code iterator()} method. Multiple instances may be returned which use the
* same linked list of {@code SecondaryIteratorEntry}. Whenever an iterator runs to the end of this list, the primary
* iterator is requested to find the next object, if it has a next object.
* <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>
*/
public class PerceptionIterator implements Iterator<H>
{
/** Last returned entry. */
private SecondaryIteratorEntry lastReturned;
/** Next entry. */
private SecondaryIteratorEntry next;
/** Constructor. */
PerceptionIterator()
{
this.next = AbstractPerceptionReiterable.this.first;
}
/** {@inheritDoc} */
@Override
public boolean hasNext()
{
this.next = assureNext(this.next, this.lastReturned);
return this.next != null;
}
/** {@inheritDoc} */
@Override
public H next()
{
this.next = assureNext(this.next, this.lastReturned);
if (this.next == null)
{
throw new NoSuchElementException();
}
this.lastReturned = this.next;
this.next = this.lastReturned.next;
return this.lastReturned.getValue();
}
}
/**
* Helper method that assures that a next entry is available, if the primary iterator has a next value. This method may be
* used by any process that derives from the primary iterator.
* @param next SecondaryIteratorEntry; currently known next entry
* @param lastReturned SecondaryIteratorEntry; entry of last returned object or value
* @return IteratorEntry; next entry
*/
synchronized SecondaryIteratorEntry assureNext(final SecondaryIteratorEntry next, final SecondaryIteratorEntry lastReturned)
{
if (next != null)
{
return next;
}
if (lastReturned != null)
{
if (lastReturned.next == null)
{
if (getPrimaryIterator().hasNext())
{
addNext(getPrimaryIterator().next());
}
}
return lastReturned.next;
}
if (getPrimaryIterator().hasNext())
{
addNext(getPrimaryIterator().next());
}
return AbstractPerceptionReiterable.this.first;
}
/**
* Class for {@code primaryIterator()} to return, implemented in subclasses.
* <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>
*/
protected class PrimaryIteratorEntry implements Comparable<PrimaryIteratorEntry>
{
/** Object. */
private final U object;
/** Distance to the object. */
private final Length distance;
/**
* Constructor.
* @param object U; object
* @param distance Length; distance
*/
public PrimaryIteratorEntry(final U object, final Length distance)
{
this.object = object;
this.distance = distance;
}
/** {@inheritDoc} */
@Override
public int compareTo(final PrimaryIteratorEntry o)
{
return this.distance.compareTo(o.distance);
}
/**
* Returns the object.
* @return U; object
*/
protected U getObject()
{
return this.object;
}
}
/**
* Entries that make up a linked list of values for secondary iterators to iterate over.
* <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>
*/
private class SecondaryIteratorEntry
{
/** Value. */
private final U object;
/** Distance to object. */
private final Length distance;
/** Value. */
private H value;
/** Next entry. */
private SecondaryIteratorEntry next;
/**
* Constructor.
* @param object U; object
* @param distance Length; distance to object
*/
SecondaryIteratorEntry(final U object, final Length distance)
{
this.object = object;
this.distance = distance;
}
/**
* Returns the perceived version of the object.
* @return H; perceived version of the object
*/
H getValue()
{
if (this.value == null)
{
/*-
this.value = Try.assign(() -> perceive(AbstractPerceptionReiterable.this.getGtu(), this.object, this.distance),
"Exception during perception of object.");
*/
try
{
this.value = perceive(AbstractPerceptionReiterable.this.getGtu(), this.object, this.distance);
}
catch (Exception e)
{
e.printStackTrace();
}
}
return this.value;
}
}
}