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.Try;
9 import org.opentrafficsim.base.parameters.ParameterException;
10 import org.opentrafficsim.core.gtu.GTUException;
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-2019 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-2019 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 SecondaryIteratorEntry; currently known next entry
222 * @param lastReturned SecondaryIteratorEntry; 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-2019 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-2019 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 }