1 package org.opentrafficsim.road.gtu.lane.perception.structure;
2
3 import java.util.Collection;
4 import java.util.Iterator;
5 import java.util.LinkedHashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.NoSuchElementException;
9 import java.util.function.BiFunction;
10 import java.util.function.Function;
11
12 import org.djunits.value.vdouble.scalar.Length;
13 import org.djutils.exceptions.Throw;
14 import org.opentrafficsim.road.gtu.lane.perception.structure.NavigatingIterable.Entry;
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 public class NavigatingIterable<T, L extends LaneRecordInterface<L>> implements Iterable<Entry<T>>
37 {
38
39
40 private final Class<T> clazz;
41
42
43 private final Length range;
44
45
46 private final Collection<L> start;
47
48
49 private final Function<L, Collection<L>> navigator;
50
51
52 private final Function<L, List<?>> lister;
53
54
55 private final BiFunction<T, L, Length> distancer;
56
57
58
59
60
61
62
63
64
65
66 public NavigatingIterable(final Class<T> clazz, final Length range, final Collection<L> start,
67 final Function<L, Collection<L>> navigator, final Function<L, List<?>> lister,
68 final BiFunction<T, L, Length> distancer)
69 {
70 this.clazz = clazz;
71 this.range = range;
72 this.start = start;
73 this.navigator = navigator;
74 this.lister = lister;
75 this.distancer = distancer;
76 }
77
78 @Override
79 public Iterator<Entry<T>> iterator()
80 {
81
82 Map<L, ObjectIterator> map = new LinkedHashMap<>();
83 for (L startRecord : this.start)
84 {
85 map.put(startRecord, new ObjectIterator(startRecord, NavigatingIterable.this.clazz, NavigatingIterable.this.lister,
86 NavigatingIterable.this.distancer));
87 }
88 return new Iterator<Entry<T>>()
89 {
90
91 private Entry<T> next;
92
93 @Override
94 public boolean hasNext()
95 {
96 if (this.next != null)
97 {
98 return true;
99 }
100
101 Map<L, ObjectIterator> mapCopy = new LinkedHashMap<>(map);
102 for (Map.Entry<L, ObjectIterator> mapEntry : mapCopy.entrySet())
103 {
104 if (!mapEntry.getValue().hasNext())
105 {
106 updateMapRecursive(mapEntry.getKey());
107 }
108 }
109 if (map.isEmpty())
110 {
111 this.next = null;
112 }
113 else if (map.size() == 1)
114 {
115 this.next = map.values().iterator().next().next();
116 }
117 else
118 {
119
120 Length minDistance = Length.POSITIVE_INFINITY;
121 ObjectIterator closestObjectIterator = null;
122 for (ObjectIterator objectIterator : map.values())
123 {
124 Entry<T> entry = objectIterator.poll();
125 if (entry.distance().lt(minDistance))
126 {
127 minDistance = entry.distance();
128 closestObjectIterator = objectIterator;
129 }
130 }
131 this.next = closestObjectIterator.next();
132 }
133 if (this.next != null && this.next.distance().gt(NavigatingIterable.this.range))
134 {
135 this.next = null;
136 }
137 return this.next != null;
138 }
139
140 @Override
141 public Entry<T> next()
142 {
143 Throw.when(!hasNext(), NoSuchElementException.class, "No more object of type %s.",
144 NavigatingIterable.this.clazz);
145 Entry<T> n = this.next;
146 this.next = null;
147 return n;
148 }
149
150
151
152
153
154
155 private void updateMapRecursive(final L record)
156 {
157 if (!map.containsKey(record) || map.get(record).hasNext())
158 {
159 return;
160 }
161 map.remove(record);
162 for (L next : NavigatingIterable.this.navigator.apply(record))
163 {
164 map.put(next, new ObjectIterator(next, NavigatingIterable.this.clazz, NavigatingIterable.this.lister,
165 NavigatingIterable.this.distancer));
166 updateMapRecursive(next);
167 }
168 }
169 };
170 }
171
172
173
174
175
176
177
178
179
180
181 private class ObjectIterator implements Iterator<Entry<T>>
182 {
183
184
185 private final L record;
186
187
188 private final Class<T> clazz;
189
190
191 private final List<?> list;
192
193
194 private int index;
195
196
197 private final BiFunction<T, L, Length> distancer;
198
199
200 private Entry<T> poll;
201
202
203
204
205
206
207
208
209 ObjectIterator(final L record, final Class<T> clazz, final Function<L, List<?>> lister,
210 final BiFunction<T, L, Length> distancer)
211 {
212 this.record = record;
213 this.clazz = clazz;
214 this.list = lister.apply(record);
215 this.distancer = distancer;
216 }
217
218 @Override
219 public boolean hasNext()
220 {
221 while (this.index < this.list.size() && !this.list.get(this.index).getClass().isAssignableFrom(this.clazz))
222 {
223 this.index++;
224 }
225 return this.index < this.list.size();
226 }
227
228 @Override
229 public Entry<T> next()
230 {
231 Entry<T> entry = poll();
232 this.index++;
233 this.poll = null;
234 return entry;
235 }
236
237
238
239
240
241 public Entry<T> poll()
242 {
243 Throw.when(!hasNext(), NoSuchElementException.class, "No more object of type %s.", this.clazz);
244 if (this.poll == null)
245 {
246 @SuppressWarnings("unchecked")
247 T t = (T) this.list.get(this.index);
248 this.poll = new Entry<>(this.distancer.apply(t, this.record), this.record.getMergeDistance(), t);
249 }
250 return this.poll;
251 }
252
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269 public record Entry<T>(Length distance, Length merge, T object)
270 {
271 }
272
273 }