1 package org.opentrafficsim.road.gtu.lane.perception.structure;
2
3 import java.util.Collection;
4 import java.util.Collections;
5 import java.util.Deque;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.LinkedHashSet;
9 import java.util.LinkedList;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.NoSuchElementException;
13 import java.util.Set;
14 import java.util.SortedSet;
15 import java.util.TreeSet;
16 import java.util.function.BiFunction;
17 import java.util.function.Function;
18
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djunits.value.vdouble.scalar.Time;
21 import org.djutils.exceptions.Throw;
22 import org.djutils.exceptions.Try;
23 import org.opentrafficsim.core.gtu.RelativePosition;
24 import org.opentrafficsim.core.network.LateralDirectionality;
25 import org.opentrafficsim.core.network.Link;
26 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
27 import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
28 import org.opentrafficsim.road.network.lane.Lane;
29 import org.opentrafficsim.road.network.lane.LanePosition;
30 import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
31
32
33
34
35
36
37
38
39
40 public class LaneStructure
41 {
42
43
44 private final LaneBasedGtu gtu;
45
46
47 private Length upstream;
48
49
50 private Length downstream;
51
52
53 private Time updated = null;
54
55
56 private final Map<RelativeLane, Set<LaneRecord>> crossSection = new LinkedHashMap<>();
57
58
59 private final Map<RelativeLane, LaneRecord> rootCrossSection = new LinkedHashMap<>();
60
61
62
63
64
65
66
67
68 public LaneStructure(final LaneBasedGtu gtu, final Length upstream, final Length downstream)
69 {
70 this.gtu = gtu;
71 this.upstream = upstream;
72 this.downstream = downstream;
73 }
74
75
76
77
78
79
80
81
82
83
84
85
86 public <T extends LaneBasedObject> Iterable<Entry<T>> getDownstreamObjects(final RelativeLane relativeLane,
87 final Class<T> clazz, final RelativePosition.Type position, final boolean onRoute)
88 {
89 update();
90 Length dx = LaneStructure.this.gtu.getRelativePositions().get(position).dx();
91 return new NavigatingIterable<>(relativeLane, clazz, this.downstream, (record) -> startDownstream(record, position),
92 (record) ->
93 {
94
95 Set<LaneRecord> set = record.getNext();
96 set.removeIf((r) -> onRoute && !r.isOnRoute(LaneStructure.this.gtu.getStrategicalPlanner().getRoute()));
97 return set;
98 }, (record) ->
99 {
100
101 List<LaneBasedObject> list = record.getLane().getLaneBasedObjects();
102 if (list.isEmpty())
103 {
104 return list;
105 }
106 Length pos = record.getStartDistance().neg().plus(dx);
107 int from = 0;
108 while (from < list.size()
109 && list.get(from).getLongitudinalPosition().plus(list.get(from).getLength()).lt(pos))
110 {
111 from++;
112 }
113 if (from > list.size() - 1)
114 {
115 return Collections.EMPTY_LIST;
116 }
117 return list.subList(from, list.size());
118 }, (t, r) -> r.getStartDistance().plus(t.getLongitudinalPosition()).minus(dx));
119 }
120
121
122
123
124
125
126
127
128
129
130
131 public <T extends LaneBasedObject> Iterable<Entry<T>> getUpstreamObjects(final RelativeLane relativeLane,
132 final Class<T> clazz, final RelativePosition.Type position)
133 {
134 update();
135 Length dx = LaneStructure.this.gtu.getRelativePositions().get(position).dx();
136 return new NavigatingIterable<T>(relativeLane, clazz, this.upstream, (record) -> startUpstream(record, position),
137 (record) ->
138 {
139
140 Set<LaneRecord> set = new LinkedHashSet<>(record.getPrev());
141 set.addAll(record.lateral());
142 return set;
143 }, (record) ->
144 {
145
146 List<LaneBasedObject> list = record.getLane().getLaneBasedObjects();
147 if (list.isEmpty())
148 {
149 return list;
150 }
151 Length pos = record.getStartDistance().neg().plus(dx);
152 int to = list.size();
153 while (to >= 0 && list.get(to).getLongitudinalPosition().gt(pos))
154 {
155 to--;
156 }
157 if (to < 0)
158 {
159 return Collections.EMPTY_LIST;
160 }
161 list = list.subList(0, to);
162 Collections.reverse(list);
163 return list;
164 }, (t, r) -> r.getStartDistance().plus(t.getLongitudinalPosition()).plus(dx).neg());
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178 public Iterable<Entry<LaneBasedGtu>> getDownstreamGtus(final RelativeLane relativeLane,
179 final RelativePosition.Type egoPosition, final RelativePosition.Type otherPosition,
180 final RelativePosition.Type egoDistancePosition, final RelativePosition.Type otherDistancePosition)
181 {
182 update();
183 Length dx = LaneStructure.this.gtu.getRelativePositions().get(egoPosition).dx();
184 Length dxDistance = LaneStructure.this.gtu.getRelativePositions().get(egoDistancePosition).dx();
185 return new NavigatingIterable<>(relativeLane, LaneBasedGtu.class, this.downstream,
186 (record) -> startDownstream(record, egoPosition), (record) -> record.getNext(), (record) ->
187 {
188
189 List<LaneBasedGtu> gtus = record.getLane().getGtuList().toList();
190 if (gtus.isEmpty())
191 {
192 return gtus;
193 }
194 int from = 0;
195 Length pos = Length.max(record.getStartDistance().neg().plus(dx), Length.ZERO);
196 while (from < gtus.size() && (position(gtus.get(from), record, otherPosition).lt(pos)
197 || gtus.get(from).getId().equals(this.gtu.getId())))
198 {
199 from++;
200 }
201 int to = gtus.size() - 1;
202 while (to >= 0 && (position(gtus.get(to), record, otherPosition).gt(record.getLane().getLength())
203 || gtus.get(to).getId().equals(this.gtu.getId())))
204 {
205 to--;
206 }
207 if (from > to)
208 {
209 return Collections.EMPTY_LIST;
210 }
211 if (from > 0 || to < gtus.size() - 1)
212 {
213 gtus = gtus.subList(from, to + 1);
214 }
215 return gtus;
216 }, (t, r) -> r.getStartDistance().plus(position(t, r, otherDistancePosition)).minus(dxDistance));
217 }
218
219
220
221
222
223
224
225
226
227
228
229
230 public Iterable<Entry<LaneBasedGtu>> getUpstreamGtus(final RelativeLane relativeLane,
231 final RelativePosition.Type egoPosition, final RelativePosition.Type otherPosition,
232 final RelativePosition.Type egoDistancePosition, final RelativePosition.Type otherDistancePosition)
233 {
234 update();
235 Length dx = LaneStructure.this.gtu.getRelativePositions().get(egoPosition).dx();
236 Length dxDistance = LaneStructure.this.gtu.getRelativePositions().get(egoDistancePosition).dx();
237 return new NavigatingIterable<>(relativeLane, LaneBasedGtu.class, this.upstream,
238 (record) -> startUpstream(record, egoPosition), (record) ->
239 {
240
241 Set<LaneRecord> set = new LinkedHashSet<>(record.getPrev());
242 set.addAll(record.lateral());
243 return set;
244 }, (record) ->
245 {
246
247 List<LaneBasedGtu> gtus = record.getLane().getGtuList().toList();
248 if (gtus.isEmpty())
249 {
250 return gtus;
251 }
252 int from = 0;
253 while (from < gtus.size() && (position(gtus.get(from), record, otherPosition).lt0()
254 || gtus.get(from).getId().equals(this.gtu.getId())))
255 {
256 from++;
257 }
258 int to = gtus.size() - 1;
259 Length pos = Length.min(record.getStartDistance().neg().plus(dx), record.getLane().getLength());
260 while (to >= 0 && (position(gtus.get(to), record, otherPosition).gt(pos)
261 || gtus.get(to).getId().equals(this.gtu.getId())))
262 {
263 to--;
264 }
265 if (from > to)
266 {
267 return Collections.EMPTY_LIST;
268 }
269 if (from > 0 || to < gtus.size() - 1)
270 {
271 gtus = gtus.subList(from, to + 1);
272 }
273 Collections.reverse(gtus);
274 return gtus;
275 }, (t, r) -> dxDistance.minus(r.getStartDistance().plus(position(t, r, otherDistancePosition))));
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289
290 public Iterable<Entry<LaneBasedGtu>> getFirstDownstreamGtus(final RelativeLane relativeLane,
291 final RelativePosition.Type egoPosition, final RelativePosition.Type otherPosition,
292 final RelativePosition.Type egoDistancePosition, final RelativePosition.Type otherDistancePosition)
293 {
294 update();
295 Length dx = LaneStructure.this.gtu.getRelativePositions().get(egoPosition).dx();
296 Length dxDistance = LaneStructure.this.gtu.getRelativePositions().get(egoDistancePosition).dx();
297 return new NavigatingIterable<>(relativeLane, LaneBasedGtu.class, this.downstream,
298 (record) -> startDownstream(record, egoPosition), (record) ->
299 {
300
301 return Try.assign(
302 () -> record.getLane().getGtuAhead(record.getStartDistance().neg().plus(dx), otherPosition,
303 record.getLane().getNetwork().getSimulator().getSimulatorAbsTime()),
304 "Problem with GTU") == null ? record.getNext() : new LinkedHashSet<>();
305 }, (record) ->
306 {
307
308 LaneBasedGtu down =
309 Try.assign(
310 () -> record.getLane().getGtuAhead(record.getStartDistance().neg().plus(dx), otherPosition,
311 record.getLane().getNetwork().getSimulator().getSimulatorAbsTime()),
312 "Problem with GTU");
313 return down == null ? Collections.EMPTY_LIST : List.of(down);
314 }, (t, r) -> r.getStartDistance().plus(position(t, r, otherDistancePosition)).minus(dxDistance));
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329 public Iterable<Entry<LaneBasedGtu>> getFirstUpstreamGtus(final RelativeLane relativeLane,
330 final RelativePosition.Type egoPosition, final RelativePosition.Type otherPosition,
331 final RelativePosition.Type egoDistancePosition, final RelativePosition.Type otherDistancePosition)
332 {
333 update();
334 Length dx = LaneStructure.this.gtu.getRelativePositions().get(egoPosition).dx();
335 Length dxDistance = LaneStructure.this.gtu.getRelativePositions().get(egoDistancePosition).dx();
336 return new NavigatingIterable<>(relativeLane, LaneBasedGtu.class, this.upstream,
337 (record) -> startUpstream(record, egoPosition), (record) ->
338 {
339
340
341
342 LaneBasedGtu gtu =
343 Try.assign(
344 () -> record.getLane().getGtuBehind(record.getStartDistance().neg().plus(dx), otherPosition,
345 record.getLane().getNetwork().getSimulator().getSimulatorAbsTime()),
346 "Problem with GTU");
347 Set<LaneRecord> set = new LinkedHashSet<>();
348 if (gtu == null)
349 {
350 set.addAll(record.getPrev());
351 set.addAll(record.lateral());
352 }
353 return set;
354 }, (record) ->
355 {
356
357 LaneBasedGtu up =
358 Try.assign(
359 () -> record.getLane().getGtuBehind(record.getStartDistance().neg().plus(dx), otherPosition,
360 record.getLane().getNetwork().getSimulator().getSimulatorAbsTime()),
361 "Problem with GTU");
362 return up == null ? Collections.EMPTY_LIST : List.of(up);
363 }, (t, r) -> dxDistance.minus(r.getStartDistance().plus(position(t, r, otherDistancePosition))));
364 }
365
366
367
368
369
370
371
372
373 private Collection<LaneRecord> startDownstream(final LaneRecord record, final RelativePosition.Type position)
374 {
375 if (position(LaneStructure.this.gtu, record, position).ge0())
376 {
377 return Set.of(record);
378 }
379 Set<LaneRecord> set = new LinkedHashSet<>();
380 for (LaneRecord up : record.getPrev())
381 {
382 set.addAll(startDownstream(up, position));
383 }
384 return set;
385 }
386
387
388
389
390
391
392
393
394 private Collection<LaneRecord> startUpstream(final LaneRecord record, final RelativePosition.Type position)
395 {
396 if (position(LaneStructure.this.gtu, record, position).lt(record.getLane().getLength()))
397 {
398 return Set.of(record);
399 }
400 Set<LaneRecord> set = new LinkedHashSet<>();
401 for (LaneRecord down : record.getNext())
402 {
403 set.addAll(startUpstream(down, position));
404 }
405 return set;
406 }
407
408
409
410
411
412
413
414
415 private final Length position(final LaneBasedGtu gtu, final LaneRecord record, final RelativePosition.Type positionType)
416 {
417 if (gtu.equals(LaneStructure.this.gtu))
418 {
419 return record.getStartDistance().neg().plus(gtu.getRelativePositions().get(positionType).dx());
420 }
421 return Try.assign(() -> gtu.position(record.getLane(), gtu.getRelativePositions().get(positionType)),
422 "Unable to obtain position %s of GTU.", positionType);
423 }
424
425
426
427
428 private synchronized void update()
429 {
430 if (this.updated != null && this.updated.equals(this.gtu.getSimulator().getSimulatorAbsTime()))
431 {
432 return;
433 }
434
435 this.crossSection.clear();
436 Set<Lane> visited = new LinkedHashSet<>();
437 Deque<LaneRecord> downQueue = new LinkedList<>();
438 Deque<LaneRecord> upQueue = new LinkedList<>();
439 Deque<LaneRecord> latDownQueue = new LinkedList<>();
440 Deque<LaneRecord> latUpQueue = new LinkedList<>();
441 LanePosition position = Try.assign(() -> this.gtu.getReferencePosition(), "GTU does not have a reference position.");
442 LaneRecord root = new LaneRecord(position.lane(), RelativeLane.CURRENT, position.position().neg(), Length.ZERO);
443 visited.add(position.lane());
444 addToCrossSection(root);
445 downQueue.add(root);
446 upQueue.add(root);
447 latDownQueue.add(root);
448 this.rootCrossSection.put(root.getRelativeLane(), root);
449 while (!downQueue.isEmpty() || !upQueue.isEmpty() || !latDownQueue.isEmpty() || !latUpQueue.isEmpty())
450 {
451 if (!downQueue.isEmpty())
452 {
453 LaneRecord record = downQueue.poll();
454 Set<Lane> downstreamLanes = record.getLane().nextLanes(null);
455 if (!record.getLane().getType().isCompatible(this.gtu.getType()))
456 {
457
458
459
460
461
462 downstreamLanes.removeAll(record.getLane().nextLanes(this.gtu.getType()));
463 }
464 for (Lane lane : downstreamLanes)
465 {
466 LaneRecord down = new LaneRecord(lane, record.getRelativeLane(), record.getEndDistance(), Length.ZERO);
467 record.addNext(down);
468 down.addPrev(record);
469 visited.add(lane);
470 addToCrossSection(down);
471 if (down.getEndDistance().lt(this.downstream))
472 {
473 downQueue.add(down);
474 }
475 latDownQueue.add(down);
476 }
477 }
478 else if (!upQueue.isEmpty())
479 {
480 LaneRecord record = upQueue.poll();
481 for (Lane lane : record.getLane().prevLanes(null))
482 {
483
484
485
486
487 if (!visited.contains(lane) || record.getMergeDistance().eq0())
488 {
489 LaneRecord up = new LaneRecord(lane, record.getRelativeLane(),
490 record.getStartDistance().minus(lane.getLength()), record.getMergeDistance());
491 record.addPrev(up);
492 up.addNext(record);
493 visited.add(lane);
494 addToCrossSection(up);
495 if (up.getStartDistance().neg().plus(up.getMergeDistance()).lt(this.upstream))
496 {
497 upQueue.add(up);
498 }
499 latUpQueue.add(up);
500 }
501 }
502 }
503 else
504 {
505 boolean down;
506 Deque<LaneRecord> latQueue;
507 if (!latDownQueue.isEmpty())
508 {
509 down = true;
510 latQueue = latDownQueue;
511 }
512 else
513 {
514 down = false;
515 latQueue = latUpQueue;
516 }
517 LaneRecord record = latQueue.poll();
518 for (LateralDirectionality latDirection : new LateralDirectionality[] {LateralDirectionality.LEFT,
519 LateralDirectionality.RIGHT})
520 {
521 for (Lane lane : record.getLane().accessibleAdjacentLanesPhysical(latDirection, null))
522 {
523 if (!visited.contains(lane))
524 {
525
526
527
528
529
530
531
532 RelativeLane relativeLane = !down ? record.getRelativeLane() : (latDirection.isLeft()
533 ? record.getRelativeLane().getLeft() : record.getRelativeLane().getRight());
534
535
536
537
538
539
540 Length startDistance;
541 if (record.getStartDistance().lt0() && record.getEndDistance().gt0())
542 {
543 startDistance = lane.getLength()
544 .times(record.getStartDistance().neg().si / record.getLane().getLength().si).neg();
545 }
546 else if (down)
547 {
548 startDistance = record.getStartDistance();
549 }
550 else
551 {
552 startDistance = record.getEndDistance().minus(lane.getLength());
553 }
554
555
556
557
558
559 Length mergeDistance;
560 if (down && record.getStartDistance().gt0())
561 {
562 if (!getUpstreamLinks(lane).equals(getUpstreamLinks(record.getLane())))
563 {
564 mergeDistance = record.getStartDistance();
565 }
566 else
567 {
568 mergeDistance = Length.ZERO;
569 }
570 }
571 else
572 {
573 mergeDistance = record.getMergeDistance();
574 }
575 LaneRecord lat = new LaneRecord(lane, relativeLane, startDistance, mergeDistance);
576 if (!down)
577 {
578 record.addLateral(lat);
579 }
580 visited.add(lane);
581 addToCrossSection(lat);
582
583
584 if (this.rootCrossSection.containsValue(record))
585 {
586
587 this.rootCrossSection.put(lat.getRelativeLane(), lat);
588 latDownQueue.add(lat);
589 if (lat.getEndDistance().lt(this.downstream))
590 {
591 downQueue.add(lat);
592 }
593 if (lat.getStartDistance().neg().lt(this.upstream))
594 {
595 upQueue.add(lat);
596 }
597 }
598 else if (down)
599 {
600 latDownQueue.add(lat);
601 if (lat.getEndDistance().lt(this.downstream))
602 {
603 downQueue.add(lat);
604 if (mergeDistance.gt0())
605 {
606 upQueue.add(lat);
607 }
608 }
609 }
610 else
611 {
612 latUpQueue.add(lat);
613 if (lat.getStartDistance().neg().plus(lat.getMergeDistance()).lt(this.upstream))
614 {
615 upQueue.add(lat);
616 }
617 }
618
619 }
620 }
621 }
622 }
623 }
624 this.updated = this.gtu.getSimulator().getSimulatorAbsTime();
625 }
626
627
628
629
630
631
632 private void addToCrossSection(final LaneRecord record)
633 {
634 if (record.getStartDistance().le0() && record.getEndDistance().gt0())
635 {
636 this.crossSection.computeIfAbsent(record.getRelativeLane(), (r) -> new LinkedHashSet<>()).add(record);
637 }
638 }
639
640
641
642
643
644
645 private Set<Link> getUpstreamLinks(final Lane lane)
646 {
647 Set<Link> set = new LinkedHashSet<>();
648 for (Lane prev : lane.prevLanes(null))
649 {
650 set.add(prev.getLink());
651 }
652 return set;
653 }
654
655
656
657
658
659 public SortedSet<RelativeLane> getRootCrossSection()
660 {
661 update();
662 return new TreeSet<>(this.rootCrossSection.keySet());
663 }
664
665
666
667
668
669
670 public boolean exists(final RelativeLane lane)
671 {
672 update();
673 return this.crossSection.containsKey(lane);
674 }
675
676
677
678
679
680
681 public LaneRecord getRootRecord(final RelativeLane lane)
682 {
683 update();
684 return this.rootCrossSection.get(lane);
685 }
686
687
688
689
690
691
692 public Set<LaneRecord> getCrossSectionRecords(final RelativeLane lane)
693 {
694 return new LinkedHashSet<>(this.crossSection.get(lane));
695 }
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719 private class NavigatingIterable<T> implements Iterable<Entry<T>>
720 {
721
722 private final RelativeLane relativeLane;
723
724
725 private final Class<T> clazz;
726
727
728 private final Length range;
729
730
731 private final Function<LaneRecord, Collection<LaneRecord>> starter;
732
733
734 private final Function<LaneRecord, Collection<LaneRecord>> navigator;
735
736
737 private final Function<LaneRecord, List<?>> lister;
738
739
740 private final BiFunction<T, LaneRecord, Length> distancer;
741
742
743
744
745
746
747
748
749
750
751
752 public NavigatingIterable(final RelativeLane relativeLane, final Class<T> clazz, final Length range,
753 final Function<LaneRecord, Collection<LaneRecord>> starter,
754 final Function<LaneRecord, Collection<LaneRecord>> navigator, final Function<LaneRecord, List<?>> lister,
755 final BiFunction<T, LaneRecord, Length> distancer)
756 {
757 this.relativeLane = relativeLane;
758 this.clazz = clazz;
759 this.range = range;
760 this.starter = starter;
761 this.navigator = navigator;
762 this.lister = lister;
763 this.distancer = distancer;
764 }
765
766
767 @Override
768 public Iterator<Entry<T>> iterator()
769 {
770
771 Map<LaneRecord, ObjectIterator<T>> map = new LinkedHashMap<>();
772
773 for (LaneRecord record : LaneStructure.this.crossSection.computeIfAbsent(NavigatingIterable.this.relativeLane,
774 (l) -> new LinkedHashSet<>()))
775 {
776 for (LaneRecord start : this.starter.apply(record))
777 {
778 map.put(start, new ObjectIterator<>(start, NavigatingIterable.this.clazz, NavigatingIterable.this.lister,
779 NavigatingIterable.this.distancer));
780 }
781 }
782 return new Iterator<Entry<T>>()
783 {
784
785 private Entry<T> next;
786
787
788 @Override
789 public boolean hasNext()
790 {
791 if (this.next != null)
792 {
793 return true;
794 }
795
796 Map<LaneRecord, ObjectIterator<T>> mapCopy = new LinkedHashMap<>(map);
797 for (Map.Entry<LaneRecord, ObjectIterator<T>> mapEntry : mapCopy.entrySet())
798 {
799 if (!mapEntry.getValue().hasNext())
800 {
801 updateMapRecursive(mapEntry.getKey());
802 }
803 }
804 if (map.isEmpty())
805 {
806 this.next = null;
807 }
808 else if (map.size() == 1)
809 {
810 this.next = map.values().iterator().next().next();
811 }
812 else
813 {
814
815 Length minDistance = Length.POSITIVE_INFINITY;
816 ObjectIterator<T> closestObjectIterator = null;
817 for (ObjectIterator<T> objectIterator : map.values())
818 {
819 Entry<T> entry = objectIterator.poll();
820 if (entry.distance().lt(minDistance))
821 {
822 minDistance = entry.distance();
823 closestObjectIterator = objectIterator;
824 }
825 }
826 this.next = closestObjectIterator.next();
827 }
828 if (this.next != null && this.next.distance().gt(NavigatingIterable.this.range))
829 {
830 this.next = null;
831 }
832 return this.next != null;
833 }
834
835
836 @Override
837 public Entry<T> next()
838 {
839 Throw.when(!hasNext(), NoSuchElementException.class, "No more object of type %s.",
840 NavigatingIterable.this.clazz);
841 Entry<T> n = this.next;
842 this.next = null;
843 return n;
844 }
845
846
847
848
849
850
851 private void updateMapRecursive(final LaneRecord record)
852 {
853 if (!map.containsKey(record) || map.get(record).hasNext())
854 {
855 return;
856 }
857 map.remove(record);
858 for (LaneRecord next : NavigatingIterable.this.navigator.apply(record))
859 {
860 map.put(next, new ObjectIterator<>(next, NavigatingIterable.this.clazz, NavigatingIterable.this.lister,
861 NavigatingIterable.this.distancer));
862 updateMapRecursive(next);
863 }
864 }
865 };
866 }
867 }
868
869
870
871
872
873
874
875
876
877
878
879 private class ObjectIterator<T> implements Iterator<Entry<T>>
880 {
881
882 private final LaneRecord record;
883
884
885 private final Class<T> clazz;
886
887
888 private final List<?> list;
889
890
891 private int index;
892
893
894 private final BiFunction<T, LaneRecord, Length> distancer;
895
896
897 private Entry<T> poll;
898
899
900
901
902
903
904
905
906 public ObjectIterator(final LaneRecord record, final Class<T> clazz, final Function<LaneRecord, List<?>> lister,
907 final BiFunction<T, LaneRecord, Length> distancer)
908 {
909 this.record = record;
910 this.clazz = clazz;
911 this.list = lister.apply(record);
912 this.distancer = distancer;
913 }
914
915
916 @Override
917 public boolean hasNext()
918 {
919 while (this.index < this.list.size() && !this.list.get(this.index).getClass().isAssignableFrom(this.clazz))
920 {
921 this.index++;
922 }
923 return this.index < this.list.size();
924 }
925
926
927 @Override
928 public Entry<T> next()
929 {
930 Throw.when(!hasNext(), NoSuchElementException.class, "No more object of type %s.", this.clazz);
931 Entry<T> entry = poll();
932 this.index++;
933 this.poll = null;
934 return entry;
935 }
936
937
938
939
940
941 public Entry<T> poll()
942 {
943 if (this.poll == null)
944 {
945 @SuppressWarnings("unchecked")
946 T t = (T) this.list.get(this.index);
947 this.poll = new Entry<>(this.distancer.apply(t, this.record), this.record.getMergeDistance(), t);
948 }
949 return this.poll;
950 }
951
952 }
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968 public record Entry<T>(Length distance, Length merge, T object)
969 {
970 }
971
972 }