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 }