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 }