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