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.djutils.exceptions.Try;
10  import org.opentrafficsim.base.parameters.ParameterException;
11  import org.opentrafficsim.core.gtu.GtuException;
12  import org.opentrafficsim.road.gtu.lane.perception.object.PerceivedObject;
13  import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
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-2024 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://github.com/peter-knoppers">Peter Knoppers</a>
26   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
27   * @param <O> perceiving object type (an {@code O} is perceiving a {@code U} as a {@code P})
28   * @param <P> perceived object type (an {@code O} is perceiving a {@code U} as a {@code P})
29   * @param <U> underlying object type (an {@code O} is perceiving a {@code U} as a {@code P})
30   */
31  public abstract class AbstractPerceptionReiterable<O extends LaneBasedObject, P extends PerceivedObject, U>
32          implements PerceptionCollectable<P, U>
33  {
34  
35      /** First entry. */
36      private SecondaryIteratorEntry first;
37  
38      /** Last entry generated by the primary iterator. */
39      private SecondaryIteratorEntry last;
40  
41      /** Primary iterator. */
42      private Iterator<UnderlyingDistance<U>> primaryIterator;
43  
44      /** Perceiving object. */
45      private final O perceivingObject;
46  
47      /**
48       * Constructor.
49       * @param perceivingObject perceiving object.
50       */
51      protected AbstractPerceptionReiterable(final O perceivingObject)
52      {
53          this.perceivingObject = perceivingObject;
54      }
55  
56      /**
57       * Returns the perceiving object.
58       * @return perceiving object.
59       */
60      public O getObject()
61      {
62          return this.perceivingObject;
63      }
64  
65      /**
66       * Returns the primary iterator.
67       * @return primary iterator
68       */
69      final Iterator<UnderlyingDistance<U>> getPrimaryIterator()
70      {
71          if (this.primaryIterator == null)
72          {
73              this.primaryIterator = primaryIterator();
74          }
75          return this.primaryIterator;
76      }
77  
78      /**
79       * Returns the primary iterator. This method is called once by AbstractPerceptionReiterable.
80       * @return primary iterator
81       */
82      protected abstract Iterator<UnderlyingDistance<U>> primaryIterator();
83  
84      /**
85       * Returns a perceived version of the underlying object.
86       * @param object underlying object
87       * @param distance distance to the object
88       * @return perceived version of the underlying object
89       * @throws GtuException on exception
90       * @throws ParameterException on invalid parameter value or missing parameter
91       */
92      protected abstract P perceive(U object, Length distance) throws GtuException, ParameterException;
93  
94      @Override
95      public final synchronized P first()
96      {
97          assureFirst();
98          if (this.first == null)
99          {
100             return null;
101         }
102         return this.first.getValue();
103     }
104 
105     /**
106      * Assures a first SecondaryIteratorEntry is present, if the primary iterator has any elements.
107      */
108     private synchronized void assureFirst()
109     {
110         if (this.first == null && getPrimaryIterator().hasNext())
111         {
112             addNext(getPrimaryIterator().next());
113         }
114     }
115 
116     /**
117      * Adds an iterator entry to the internal linked list.
118      * @param next next object with distance
119      */
120     final void addNext(final UnderlyingDistance<U> next)
121     {
122         SecondaryIteratorEntry entry = new SecondaryIteratorEntry(next);
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     @Override
136     public final boolean isEmpty()
137     {
138         return first() == null;
139     }
140 
141     @Override
142     public final Iterator<P> iterator()
143     {
144         return new PerceptionIterator();
145     }
146 
147     @Override
148     public final <C, I> C collect(final Supplier<I> identity, final PerceptionAccumulator<? super U, I> accumulator,
149             final Function<I, C> finalizer)
150     {
151         Intermediate<I> intermediate = new Intermediate<>(identity.get());
152         assureFirst();
153         if (this.first != null)
154         {
155             SecondaryIteratorEntry lastReturned = null;
156             SecondaryIteratorEntry next = this.first;
157             next = assureNext(next, lastReturned);
158             while (next != null && !intermediate.isStop())
159             {
160                 intermediate = accumulator.accumulate(intermediate, next.underlyingDistance.object(),
161                         next.underlyingDistance.distance());
162                 intermediate.step();
163                 lastReturned = next;
164                 next = lastReturned.next;
165                 next = assureNext(next, lastReturned);
166             }
167         }
168         return finalizer.apply(intermediate.getObject());
169     }
170 
171     @Override
172     public Iterator<U> underlying()
173     {
174         assureFirst();
175         SecondaryIteratorEntry firstInContext = this.first;
176         return new Iterator<U>()
177         {
178             /** Last returned iterator entry. */
179             private SecondaryIteratorEntry lastReturned = null;
180 
181             /** Next iterator entry. */
182             private SecondaryIteratorEntry next = firstInContext;
183 
184             @Override
185             public boolean hasNext()
186             {
187                 this.next = assureNext(this.next, this.lastReturned);
188                 return this.next != null;
189             }
190 
191             @Override
192             public U next()
193             {
194                 // this.next = assureNext(this.next, this.lastReturned);
195                 // if (this.next == null)
196                 // {
197                 // throw new NoSuchElementException();
198                 // }
199                 // this.lastReturned = this.next;
200                 // this.next = this.lastReturned.next;
201                 // return this.lastReturned.object;
202 
203                 this.lastReturned = this.next;
204                 this.next = this.lastReturned.next;
205                 this.next = assureNext(this.next, this.lastReturned);
206                 return this.lastReturned.underlyingDistance.object();
207             }
208         };
209     }
210 
211     @Override
212     public Iterator<UnderlyingDistance<U>> underlyingWithDistance()
213     {
214         assureFirst();
215         SecondaryIteratorEntry firstInContext = this.first;
216         return new Iterator<UnderlyingDistance<U>>()
217         {
218             /** Last returned iterator entry. */
219             private SecondaryIteratorEntry lastReturned = null;
220 
221             /** Next iterator entry. */
222             private SecondaryIteratorEntry next = firstInContext;
223 
224             @Override
225             public boolean hasNext()
226             {
227                 this.next = assureNext(this.next, this.lastReturned);
228                 return this.next != null;
229             }
230 
231             @Override
232             public UnderlyingDistance<U> next()
233             {
234                 this.lastReturned = this.next;
235                 this.next = this.lastReturned.next;
236                 this.next = assureNext(this.next, this.lastReturned);
237                 return new UnderlyingDistance<>(this.lastReturned.underlyingDistance.object(),
238                         this.lastReturned.underlyingDistance.distance());
239             }
240         };
241     }
242 
243     /**
244      * This iterator is returned to callers of the {@code iterator()} method. Multiple instances may be returned which use the
245      * same linked list of {@code SecondaryIteratorEntry}. Whenever an iterator runs to the end of this list, the primary
246      * iterator is requested to find the next object, if it has a next object.
247      * <p>
248      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
249      * <br>
250      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
251      * </p>
252      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
253      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
254      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
255      */
256     public class PerceptionIterator implements Iterator<P>
257     {
258 
259         /** Last returned entry. */
260         private SecondaryIteratorEntry lastReturned;
261 
262         /** Next entry. */
263         private SecondaryIteratorEntry next;
264 
265         /** Constructor. */
266         PerceptionIterator()
267         {
268             this.next = AbstractPerceptionReiterable.this.first;
269         }
270 
271         @Override
272         public boolean hasNext()
273         {
274             this.next = assureNext(this.next, this.lastReturned);
275             return this.next != null;
276         }
277 
278         @Override
279         public P next()
280         {
281             this.next = assureNext(this.next, this.lastReturned);
282             if (this.next == null)
283             {
284                 throw new NoSuchElementException();
285             }
286             this.lastReturned = this.next;
287             this.next = this.lastReturned.next;
288             return this.lastReturned.getValue();
289         }
290 
291     }
292 
293     /**
294      * Helper method that assures that a next entry is available, if the primary iterator has a next value. This method may be
295      * used by any process that derives from the primary iterator.
296      * @param next currently known next entry
297      * @param lastReturned entry of last returned object or value
298      * @return next entry
299      */
300     synchronized SecondaryIteratorEntry assureNext(final SecondaryIteratorEntry next, final SecondaryIteratorEntry lastReturned)
301     {
302         if (next != null)
303         {
304             return next;
305         }
306         if (lastReturned != null)
307         {
308             if (lastReturned.next == null)
309             {
310                 if (getPrimaryIterator().hasNext())
311                 {
312                     addNext(getPrimaryIterator().next());
313                 }
314             }
315             return lastReturned.next;
316         }
317         if (getPrimaryIterator().hasNext())
318         {
319             addNext(getPrimaryIterator().next());
320         }
321         return AbstractPerceptionReiterable.this.first;
322     }
323 
324     /**
325      * Entries that make up a linked list of values for secondary iterators to iterate over.
326      * <p>
327      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
328      * <br>
329      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
330      * </p>
331      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
332      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
333      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
334      */
335     private class SecondaryIteratorEntry
336     {
337         /** Value. */
338         private final UnderlyingDistance<U> underlyingDistance;
339 
340         /** Value. */
341         private P value;
342 
343         /** Next entry. */
344         private SecondaryIteratorEntry next;
345 
346         /**
347          * Constructor.
348          * @param underlyingDistance object with distance to object
349          */
350         SecondaryIteratorEntry(final UnderlyingDistance<U> underlyingDistance)
351         {
352             this.underlyingDistance = underlyingDistance;
353         }
354 
355         /**
356          * Returns the perceived version of the object.
357          * @return perceived version of the object
358          */
359         P getValue()
360         {
361             if (this.value == null)
362             {
363                 this.value = Try.assign(() -> perceive(this.underlyingDistance.object(), this.underlyingDistance.distance()),
364                         "Exception during perception of object.");
365             }
366             return this.value;
367         }
368     }
369 
370 }