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.core.gtu.Try;
11  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
12  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
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-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
21   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
22   * <p>
23   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 20 feb. 2018 <br>
24   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
25   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
26   * @author <a href="http://www.transport.citg.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      SecondaryIteratorEntry first;
35  
36      /** Last entry generated by the primary iterator. */
37      SecondaryIteratorEntry last;
38  
39      /** Primary iterator. */
40      private Iterator<PrimaryIteratorEntry> primaryIterator;
41  
42      /** Perceiving GTU. */
43      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 primary iterator.
56       * @return Iterator; primary iterator
57       */
58      final Iterator<PrimaryIteratorEntry> getPrimaryIterator()
59      {
60          if (this.primaryIterator == null)
61          {
62              this.primaryIterator = primaryIterator();
63          }
64          return this.primaryIterator;
65      }
66  
67      /**
68       * Returns the primary iterator. This method is called once by AbstractPerceptionReiterable.
69       * @return Iterator; primary iterator
70       */
71      protected abstract Iterator<PrimaryIteratorEntry> primaryIterator();
72  
73      /**
74       * Returns a perceived version of the underlying object.
75       * @param perceivingGtu LaneBasedGTU; perceiving GTU
76       * @param object U; underlying object
77       * @param distance Length; distance to the object
78       * @return H; perceived version of the underlying object
79       * @throws GTUException on exception
80       * @throws ParameterException on invalid parameter value or missing parameter
81       */
82      protected abstract H perceive(LaneBasedGTU perceivingGtu, U object, Length distance)
83              throws GTUException, ParameterException;
84  
85      /** {@inheritDoc} */
86      @Override
87      public final synchronized H first()
88      {
89          assureFirst();
90          if (this.first == null)
91          {
92              return null;
93          }
94          return this.first.getValue();
95      }
96  
97      /**
98       * Assures a first SecondaryIteratorEntry is present, if the primary iterator has any elements.
99       */
100     private synchronized void assureFirst()
101     {
102         if (this.first == null && getPrimaryIterator().hasNext())
103         {
104             addNext(getPrimaryIterator().next());
105         }
106     }
107 
108     /**
109      * Adds an iterator entry to the internal linked list.
110      * @param next PrimaryIteratorEntry; next object
111      */
112     final void addNext(final PrimaryIteratorEntry next)
113     {
114         SecondaryIteratorEntry entry = new SecondaryIteratorEntry(next.object, next.distance);
115         if (AbstractPerceptionReiterable.this.last == null)
116         {
117             AbstractPerceptionReiterable.this.first = entry;
118             AbstractPerceptionReiterable.this.last = entry;
119         }
120         else
121         {
122             AbstractPerceptionReiterable.this.last.next = entry;
123             AbstractPerceptionReiterable.this.last = entry;
124         }
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     public final boolean isEmpty()
130     {
131         return first() == null;
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public final Iterator<H> iterator()
137     {
138         return new PerceptionIterator();
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     public final <C, I> C collect(final Supplier<I> identity, final PerceptionAccumulator<? super U, I> accumulator,
144             final PerceptionFinalizer<C, I> finalizer)
145     {
146         Intermediate<I> intermediate = new Intermediate<>(identity.get());
147         assureFirst();
148         if (this.first != null)
149         {
150             SecondaryIteratorEntry lastReturned = null;
151             SecondaryIteratorEntry next = this.first;
152             next = assureNext(next, lastReturned);
153             while (next != null && !intermediate.isStop())
154             {
155                 intermediate = accumulator.accumulate(intermediate, next.object, next.distance);
156                 intermediate.step();
157                 lastReturned = next;
158                 next = lastReturned.next;
159                 next = assureNext(next, lastReturned);
160             }
161         }
162         return finalizer.collect(intermediate.getObject());
163     }
164 
165     /**
166      * This iterator is returned to callers of the {@code iterator()} method. Multiple instances may be returned which use the
167      * same linked list of {@code SecondaryIteratorEntry}. Whenever an iterator runs to the end of this list, the primary
168      * iterator is requested to find the next object, if it has a next object.
169      * <p>
170      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
171      * <br>
172      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
173      * <p>
174      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 feb. 2018 <br>
175      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
176      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
177      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
178      */
179     public class PerceptionIterator implements Iterator<H>
180     {
181 
182         /** Last returned entry. */
183         SecondaryIteratorEntry lastReturned;
184 
185         /** Next entry. */
186         SecondaryIteratorEntry next;
187 
188         /** Constructor. */
189         PerceptionIterator()
190         {
191             this.next = AbstractPerceptionReiterable.this.first;
192         }
193 
194         /** {@inheritDoc} */
195         @Override
196         public boolean hasNext()
197         {
198             this.next = assureNext(this.next, this.lastReturned);
199             return this.next != null;
200         }
201 
202         /** {@inheritDoc} */
203         @Override
204         public H next()
205         {
206             this.next = assureNext(this.next, this.lastReturned);
207             if (this.next == null)
208             {
209                 throw new NoSuchElementException();
210             }
211             this.lastReturned = this.next;
212             this.next = this.lastReturned.next;
213             return this.lastReturned.getValue();
214         }
215 
216     }
217 
218     /**
219      * Helper method that assures that a next entry is available, if the primary iterator has a next value. This method may be
220      * used by any process that derives from the primary iterator.
221      * @param next IteratorEntry; currently known next entry
222      * @param lastReturned IteratorEntry; entry of last returned object or value
223      * @return IteratorEntry; next entry
224      */
225     synchronized SecondaryIteratorEntry assureNext(final SecondaryIteratorEntry next, final SecondaryIteratorEntry lastReturned)
226     {
227         if (next == null && getPrimaryIterator().hasNext())
228         {
229             addNext(getPrimaryIterator().next());
230             if (lastReturned == null)
231             {
232                 return AbstractPerceptionReiterable.this.first;
233             }
234             else
235             {
236                 return lastReturned.next;
237             }
238         }
239         return next;
240     }
241 
242     /**
243      * Class for {@code primaryIterator()} to return, implemented in subclasses.
244      * <p>
245      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
246      * <br>
247      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
248      * <p>
249      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 28 feb. 2018 <br>
250      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
251      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
252      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
253      */
254     protected class PrimaryIteratorEntry implements Comparable<PrimaryIteratorEntry>
255     {
256         /** Object. */
257         final U object;
258 
259         /** Distance to the object. */
260         final Length distance;
261 
262         /**
263          * Constructor.
264          * @param object U; object
265          * @param distance Length; distance
266          */
267         public PrimaryIteratorEntry(final U object, final Length distance)
268         {
269             this.object = object;
270             this.distance = distance;
271         }
272 
273         /** {@inheritDoc} */
274         @Override
275         public int compareTo(final PrimaryIteratorEntry o)
276         {
277             return this.distance.compareTo(o.distance);
278         }
279     }
280 
281     /**
282      * Entries that make up a linked list of values for secondary iterators to iterate over.
283      * <p>
284      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
285      * <br>
286      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
287      * <p>
288      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 feb. 2018 <br>
289      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
290      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
291      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
292      */
293     private class SecondaryIteratorEntry
294     {
295         /** Value. */
296         final U object;
297 
298         /** Distance to object. */
299         final Length distance;
300 
301         /** Value. */
302         private H value;
303 
304         /** Next entry. */
305         SecondaryIteratorEntry next;
306 
307         /**
308          * Constructor.
309          * @param object U; object
310          * @param distance Length; distance to object
311          */
312         SecondaryIteratorEntry(final U object, final Length distance)
313         {
314             this.object = object;
315             this.distance = distance;
316         }
317 
318         /**
319          * Returns the perceived version of the object.
320          * @return H; perceived version of the object
321          */
322         H getValue()
323         {
324             if (this.value == null)
325             {
326                 this.value = Try.assign(() -> perceive(AbstractPerceptionReiterable.this.gtu, this.object, this.distance),
327                         "Exception during perception of object.");
328             }
329             return this.value;
330         }
331     }
332 
333 }