View Javadoc
1   package org.opentrafficsim.road.gtu.lane.perception;
2   
3   import java.util.Iterator;
4   import java.util.NoSuchElementException;
5   import java.util.function.Supplier;
6   
7   import org.djunits.value.vdouble.scalar.Length;
8   import org.djutils.exceptions.Throw;
9   import org.djutils.exceptions.Try;
10  import org.opentrafficsim.base.parameters.ParameterException;
11  import org.opentrafficsim.core.gtu.GtuException;
12  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
13  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
14  
15  /**
16   * This class uses a single primary iterator which a subclass defines, and makes sure that all elements are only looked up and
17   * created once. It does so by storing the elements in a linked list. All calls to {@code iterator()} return an iterator which
18   * iterates over the linked list. If an iterator runs to the end of the linked list, the primary iterator is requested to add an
19   * element if it has one.
20   * <p>
21   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
22   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
23   * </p>
24   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
25   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
26   * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
27   * @param <H> headway type
28   * @param <U> underlying object type
29   */
30  public abstract class AbstractPerceptionReiterable<H extends Headway, U> implements PerceptionCollectable<H, U>
31  {
32  
33      /** First entry. */
34      private SecondaryIteratorEntry first;
35  
36      /** Last entry generated by the primary iterator. */
37      private SecondaryIteratorEntry last;
38  
39      /** Primary iterator. */
40      private Iterator<PrimaryIteratorEntry> primaryIterator;
41  
42      /** Perceiving GTU. */
43      private final LaneBasedGtu gtu;
44  
45      /**
46       * Constructor.
47       * @param perceivingGtu LaneBasedGtu; perceiving GTU
48       */
49      protected AbstractPerceptionReiterable(final LaneBasedGtu perceivingGtu)
50      {
51          this.gtu = perceivingGtu;
52      }
53  
54      /**
55       * Returns the GTU.
56       * @return LaneBasedGtu; GTU
57       */
58      protected LaneBasedGtu getGtu()
59      {
60          return this.gtu;
61      }
62  
63      /**
64       * Returns the primary iterator.
65       * @return Iterator; primary iterator
66       */
67      final Iterator<PrimaryIteratorEntry> getPrimaryIterator()
68      {
69          if (this.primaryIterator == null)
70          {
71              this.primaryIterator = primaryIterator();
72          }
73          return this.primaryIterator;
74      }
75  
76      /**
77       * Returns the primary iterator. This method is called once by AbstractPerceptionReiterable.
78       * @return Iterator; primary iterator
79       */
80      protected abstract Iterator<PrimaryIteratorEntry> primaryIterator();
81  
82      /**
83       * Returns a perceived version of the underlying object.
84       * @param perceivingGtu LaneBasedGtu; perceiving GTU
85       * @param object U; underlying object
86       * @param distance Length; distance to the object
87       * @return H; perceived version of the underlying object
88       * @throws GtuException on exception
89       * @throws ParameterException on invalid parameter value or missing parameter
90       */
91      protected abstract H perceive(LaneBasedGtu perceivingGtu, U object, Length distance)
92              throws GtuException, ParameterException;
93  
94      /** {@inheritDoc} */
95      @Override
96      public final synchronized H first()
97      {
98          assureFirst();
99          if (this.first == null)
100         {
101             return null;
102         }
103         return this.first.getValue();
104     }
105 
106     /**
107      * Assures a first SecondaryIteratorEntry is present, if the primary iterator has any elements.
108      */
109     private synchronized void assureFirst()
110     {
111         if (this.first == null && getPrimaryIterator().hasNext())
112         {
113             addNext(getPrimaryIterator().next());
114         }
115     }
116 
117     /**
118      * Adds an iterator entry to the internal linked list.
119      * @param next PrimaryIteratorEntry; next object
120      */
121     @SuppressWarnings("synthetic-access")
122     final void addNext(final PrimaryIteratorEntry next)
123     {
124         SecondaryIteratorEntry entry = new SecondaryIteratorEntry(next.object, next.distance);
125         if (AbstractPerceptionReiterable.this.last == null)
126         {
127             AbstractPerceptionReiterable.this.first = entry;
128             AbstractPerceptionReiterable.this.last = entry;
129         }
130         else
131         {
132             AbstractPerceptionReiterable.this.last.next = entry;
133             AbstractPerceptionReiterable.this.last = entry;
134         }
135     }
136 
137     /** {@inheritDoc} */
138     @Override
139     public final boolean isEmpty()
140     {
141         return first() == null;
142     }
143 
144     /** {@inheritDoc} */
145     @Override
146     public final Iterator<H> iterator()
147     {
148         return new PerceptionIterator();
149     }
150 
151     /** {@inheritDoc} */
152     @SuppressWarnings("synthetic-access")
153     @Override
154     public final <C, I> C collect(final Supplier<I> identity, final PerceptionAccumulator<? super U, I> accumulator,
155             final PerceptionFinalizer<C, I> finalizer)
156     {
157         Intermediate<I> intermediate = new Intermediate<>(identity.get());
158         assureFirst();
159         if (this.first != null)
160         {
161             SecondaryIteratorEntry lastReturned = null;
162             SecondaryIteratorEntry next = this.first;
163             next = assureNext(next, lastReturned);
164             while (next != null && !intermediate.isStop())
165             {
166                 intermediate = accumulator.accumulate(intermediate, next.object, next.distance);
167                 intermediate.step();
168                 lastReturned = next;
169                 next = lastReturned.next;
170                 next = assureNext(next, lastReturned);
171             }
172         }
173         return finalizer.collect(intermediate.getObject());
174     }
175 
176     /** {@inheritDoc} */
177     @Override
178     public Iterator<U> underlying()
179     {
180         assureFirst();
181         SecondaryIteratorEntry firstInContext = this.first;
182         return new Iterator<U>()
183         {
184             /** Last returned iterator entry. */
185             private SecondaryIteratorEntry lastReturned = null;
186 
187             /** Next iterator entry. */
188             private SecondaryIteratorEntry next = firstInContext;
189 
190             /** {@inheritDoc} */
191             @Override
192             public boolean hasNext()
193             {
194                 this.next = assureNext(this.next, this.lastReturned);
195                 return this.next != null;
196             }
197 
198             /** {@inheritDoc} */
199             @SuppressWarnings("synthetic-access")
200             @Override
201             public U next()
202             {
203                 this.lastReturned = this.next;
204                 this.next = this.lastReturned.next;
205                 this.next = assureNext(this.next, this.lastReturned);
206                 return this.lastReturned.object;
207             }
208         };
209     }
210 
211     /** {@inheritDoc} */
212     @Override
213     public Iterator<UnderlyingDistance<U>> underlyingWithDistance()
214     {
215         assureFirst();
216         SecondaryIteratorEntry firstInContext = this.first;
217         return new Iterator<UnderlyingDistance<U>>()
218         {
219             /** Last returned iterator entry. */
220             private SecondaryIteratorEntry lastReturned = null;
221 
222             /** Next iterator entry. */
223             private SecondaryIteratorEntry next = firstInContext;
224 
225             /** {@inheritDoc} */
226             @Override
227             public boolean hasNext()
228             {
229                 this.next = assureNext(this.next, this.lastReturned);
230                 return this.next != null;
231             }
232 
233             /** {@inheritDoc} */
234             @SuppressWarnings("synthetic-access")
235             @Override
236             public UnderlyingDistance<U> next()
237             {
238                 this.lastReturned = this.next;
239                 this.next = this.lastReturned.next;
240                 this.next = assureNext(this.next, this.lastReturned);
241                 return new UnderlyingDistance<>(this.lastReturned.object, this.lastReturned.distance);
242             }
243         };
244     }
245 
246     /**
247      * This iterator is returned to callers of the {@code iterator()} method. Multiple instances may be returned which use the
248      * same linked list of {@code SecondaryIteratorEntry}. Whenever an iterator runs to the end of this list, the primary
249      * iterator is requested to find the next object, if it has a next object.
250      * <p>
251      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
252      * <br>
253      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
254      * </p>
255      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
256      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
257      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
258      */
259     public class PerceptionIterator implements Iterator<H>
260     {
261 
262         /** Last returned entry. */
263         private SecondaryIteratorEntry lastReturned;
264 
265         /** Next entry. */
266         private SecondaryIteratorEntry next;
267 
268         /** Constructor. */
269         @SuppressWarnings("synthetic-access")
270         PerceptionIterator()
271         {
272             this.next = AbstractPerceptionReiterable.this.first;
273         }
274 
275         /** {@inheritDoc} */
276         @Override
277         public boolean hasNext()
278         {
279             this.next = assureNext(this.next, this.lastReturned);
280             return this.next != null;
281         }
282 
283         /** {@inheritDoc} */
284         @SuppressWarnings("synthetic-access")
285         @Override
286         public H next()
287         {
288             this.next = assureNext(this.next, this.lastReturned);
289             if (this.next == null)
290             {
291                 throw new NoSuchElementException();
292             }
293             this.lastReturned = this.next;
294             this.next = this.lastReturned.next;
295             return this.lastReturned.getValue();
296         }
297 
298     }
299 
300     /**
301      * Helper method that assures that a next entry is available, if the primary iterator has a next value. This method may be
302      * used by any process that derives from the primary iterator.
303      * @param next SecondaryIteratorEntry; currently known next entry
304      * @param lastReturned SecondaryIteratorEntry; entry of last returned object or value
305      * @return IteratorEntry; next entry
306      */
307     @SuppressWarnings("synthetic-access")
308     synchronized SecondaryIteratorEntry assureNext(final SecondaryIteratorEntry next, final SecondaryIteratorEntry lastReturned)
309     {
310         if (next == null && getPrimaryIterator().hasNext())
311         {
312             addNext(getPrimaryIterator().next());
313             if (lastReturned == null)
314             {
315                 return AbstractPerceptionReiterable.this.first;
316             }
317             else
318             {
319                 return lastReturned.next;
320             }
321         }
322         return next;
323     }
324 
325     /**
326      * Class for {@code primaryIterator()} to return, implemented in subclasses.
327      * <p>
328      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
329      * <br>
330      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
331      * </p>
332      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
333      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
334      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
335      */
336     protected class PrimaryIteratorEntry implements Comparable<PrimaryIteratorEntry>
337     {
338         /** Object. */
339         private final U object;
340 
341         /** Distance to the object. */
342         private final Length distance;
343 
344         /**
345          * Constructor.
346          * @param object U; object
347          * @param distance Length; distance
348          */
349         public PrimaryIteratorEntry(final U object, final Length distance)
350         {
351             this.object = object;
352             this.distance = distance;
353         }
354 
355         /** {@inheritDoc} */
356         @Override
357         public int compareTo(final PrimaryIteratorEntry o)
358         {
359             return this.distance.compareTo(o.distance);
360         }
361 
362         /**
363          * Returns the object.
364          * @return U; object
365          */
366         protected U getObject()
367         {
368             return this.object;
369         }
370     }
371 
372     /**
373      * Entries that make up a linked list of values for secondary iterators to iterate over.
374      * <p>
375      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
376      * <br>
377      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
378      * </p>
379      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
380      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
381      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
382      */
383     private class SecondaryIteratorEntry
384     {
385         /** Value. */
386         private final U object;
387 
388         /** Distance to object. */
389         private final Length distance;
390 
391         /** Value. */
392         private H value;
393 
394         /** Next entry. */
395         private SecondaryIteratorEntry next;
396 
397         /**
398          * Constructor.
399          * @param object U; object
400          * @param distance Length; distance to object
401          */
402         SecondaryIteratorEntry(final U object, final Length distance)
403         {
404             this.object = object;
405             this.distance = distance;
406         }
407 
408         /**
409          * Returns the perceived version of the object.
410          * @return H; perceived version of the object
411          */
412         H getValue()
413         {
414             if (this.value == null)
415             {
416                 /*-
417                 this.value = Try.assign(() -> perceive(AbstractPerceptionReiterable.this.getGtu(), this.object, this.distance),
418                         "Exception during perception of object.");
419                 */
420                 try
421                 {
422                     this.value = perceive(AbstractPerceptionReiterable.this.getGtu(), this.object, this.distance);
423                 }
424                 catch (Exception e)
425                 {
426                     e.printStackTrace();
427                 }
428 
429             }
430             return this.value;
431         }
432     }
433 
434 }