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