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 }