Skip to content

Commit 3580f6e

Browse files
bedaHovorkaclaude
andcommitted
Fix type compatibility issues in DefaultRailWayNetGrid
Changes: - Fix putMap signature to accept java.util.Map directly - Fix PointIterator.remove() to save cell value before delegate removal - Add removeAll() override in KeySet to avoid ConcurrentModificationException - Update test code to use java.util.HashMap with explicit casts Technical details: 1. Changed putMap(Map<Point, TrackBlockPart>) to putMap(java.util.Map<Point, TrackBlockPart>) - Kotlin's Map interface was causing type inference issues with internal cast - Direct java.util.Map parameter eliminates the cast 2. Fixed PointIterator.remove() ordering bug: - Entry.getValue() calls Array2DMap.get(key) which fails after delegate.remove() - Must save cell value BEFORE removing from underlying map - This fixes NullPointerException in iterator remove operations 3. Added KeySet.removeAll() override: - AbstractSet.removeAll() iterates while calling remove(), causing ConcurrentModificationException - New implementation collects cells first, then removes in batch - Maintains reverse table consistency throughout 4. Updated tests to use java.util.HashMap with explicit casts: - mapOf() creates immutable Kotlin Map that doesn't cast properly - HashMap instances cast correctly to java.util.Map interface All tests now pass: 698 unit tests (668 passed, 30 skipped) + 86 integration tests Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent ffe4048 commit 3580f6e

3 files changed

Lines changed: 57 additions & 30 deletions

File tree

src/main/kotlin/cz/vutbr/fit/interlockSim/context/DefaultContext.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,11 @@ abstract class DefaultContext :
301301
}
302302

303303
if (builtPath != null && builtPath.isNotEmpty()) {
304-
val mapToAdd = builtPath as Map<Point, TrackBlockPart>
304+
@Suppress("UNCHECKED_CAST")
305+
val mapToAdd = builtPath as java.util.Map<Point, TrackBlockPart>
305306
getGrid().putMap(mapToAdd)
306307
@Suppress("UNCHECKED_CAST")
307-
val mapKeys: Set<Point> = (mapToAdd as java.util.Map<Point, TrackBlockPart>).keySet() as Set<Point>
308+
val mapKeys: Set<Point> = mapToAdd.keySet() as Set<Point>
308309
linesKeys[trackBlock] = mapKeys
309310
assert(!extendedUnorientedGraph.contains(key1, key2))
310311
extendedUnorientedGraph.put(key1, s1, key2, s2, trackBlock)

src/main/kotlin/cz/vutbr/fit/interlockSim/context/DefaultRailWayNetGrid.kt

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ class DefaultRailWayNetGrid(
4848

4949
override fun remove() {
5050
if (delegate is MutableIterator<*>) {
51+
// Save the cell BEFORE removing from cells map
52+
// (Entry.getValue() will fail after removal since it looks up the key)
53+
val cell = next?.value
5154
// Remove from cells map via delegate
5255
(delegate as MutableIterator<Entry<Point, Cell>>).remove()
5356
// Also remove from reverse table to maintain invariant
54-
val cell = next?.value
5557
if (cell != null) {
5658
getReverseTable().remove(cell)
5759
}
@@ -61,6 +63,33 @@ class DefaultRailWayNetGrid(
6163
}
6264
}
6365

66+
override fun removeAll(elements: Collection<Point>): Boolean {
67+
// Collect cells to remove before modifying the map
68+
// to avoid ConcurrentModificationException
69+
val cellsToRemove = mutableListOf<Cell>()
70+
val pointsToRemove = mutableSetOf<Point>()
71+
72+
for (point in elements) {
73+
val cell = getCells()[point]
74+
if (cell != null) {
75+
cellsToRemove.add(cell)
76+
pointsToRemove.add(point)
77+
}
78+
}
79+
80+
// Remove all points from cells map
81+
for (point in pointsToRemove) {
82+
getCells().remove(point)
83+
}
84+
85+
// Remove all cells from reverse table
86+
for (cell in cellsToRemove) {
87+
getReverseTable().remove(cell)
88+
}
89+
90+
return pointsToRemove.isNotEmpty()
91+
}
92+
6493
override val size: Int
6594
get() = set.size
6695
}
@@ -87,10 +116,8 @@ class DefaultRailWayNetGrid(
87116
* @param map of point to trackblock part
88117
*/
89118
@Synchronized
90-
fun putMap(map: Map<Point, TrackBlockPart>) {
91-
@Suppress("UNCHECKED_CAST")
92-
val javaMap = map as java.util.Map<Point, TrackBlockPart>
93-
val iter = javaMap.entrySet().iterator()
119+
fun putMap(map: java.util.Map<Point, TrackBlockPart>) {
120+
val iter = map.entrySet().iterator()
94121
while (iter.hasNext()) {
95122
val entry = iter.next()
96123
put(entry.key, entry.value)

src/test/kotlin/cz/vutbr/fit/interlockSim/context/DefaultRailWayNetGridTest.kt

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -426,14 +426,13 @@ class DefaultRailWayNetGridTest {
426426
val part1 = createTestTrackBlockPart()
427427
val part2 = createTestTrackBlockPart()
428428
val part3 = createTestTrackBlockPart()
429-
val map: Map<Point, TrackBlockPart> = mapOf(
430-
point1 to part1,
431-
point2 to part2,
432-
point3 to part3
433-
)
429+
val map = java.util.HashMap<Point, TrackBlockPart>()
430+
map[point1] = part1
431+
map[point2] = part2
432+
map[point3] = part3
434433

435434
// Act
436-
grid.putMap(map)
435+
grid.putMap(map as java.util.Map<Point, TrackBlockPart>)
437436

438437
// Assert - all parts should be in grid
439438
assertThat(grid.getCellAt(5, 5)).isSameInstanceAs(part1)
@@ -451,14 +450,13 @@ class DefaultRailWayNetGridTest {
451450
val part1 = createTestTrackBlockPart()
452451
val part2 = createTestTrackBlockPart()
453452
val part3 = createTestTrackBlockPart()
454-
val map: Map<Point, TrackBlockPart> = mapOf(
455-
point1 to part1,
456-
point2 to part2,
457-
point3 to part3
458-
)
453+
val map = java.util.HashMap<Point, TrackBlockPart>()
454+
map[point1] = part1
455+
map[point2] = part2
456+
map[point3] = part3
459457

460458
// Act
461-
grid.putMap(map)
459+
grid.putMap(map as java.util.Map<Point, TrackBlockPart>)
462460

463461
// Assert - all parts should be in reverse table
464462
assertThat(grid.getLocation(part1)).isEqualTo(point1)
@@ -476,12 +474,11 @@ class DefaultRailWayNetGridTest {
476474
val part1 = createTestTrackBlockPart()
477475
val part2 = createTestTrackBlockPart()
478476
val part3 = createTestTrackBlockPart()
479-
val map: Map<Point, TrackBlockPart> = mapOf(
480-
point1 to part1,
481-
point2 to part2,
482-
point3 to part3
483-
)
484-
grid.putMap(map)
477+
val map = java.util.HashMap<Point, TrackBlockPart>()
478+
map[point1] = part1
479+
map[point2] = part2
480+
map[point3] = part3
481+
grid.putMap(map as java.util.Map<Point, TrackBlockPart>)
485482

486483
// Act - remove all intermediate cells (simulate removeLine)
487484
grid.keySet().removeAll(setOf(point1, point2, point3))
@@ -497,7 +494,7 @@ class DefaultRailWayNetGridTest {
497494
@DisplayName("putMap with empty map does nothing")
498495
fun putMap_emptyMap_doesNothing() {
499496
// Act
500-
grid.putMap(emptyMap<Point, TrackBlockPart>())
497+
grid.putMap(java.util.HashMap<Point, TrackBlockPart>() as java.util.Map<Point, TrackBlockPart>)
501498

502499
// Assert
503500
assertThat(grid.isEmpty()).isTrue()
@@ -534,8 +531,9 @@ class DefaultRailWayNetGridTest {
534531
Point(8, 8), Point(9, 9)
535532
)
536533
val parts = intermediatePoints.map { createTestTrackBlockPart() }
537-
val map = intermediatePoints.zip(parts).toMap()
538-
grid.putMap(map)
534+
val map = java.util.HashMap<Point, TrackBlockPart>()
535+
intermediatePoints.zip(parts).forEach { (point, part) -> map[point] = part }
536+
grid.putMap(map as java.util.Map<Point, TrackBlockPart>)
539537

540538
// Act & Assert - all points should pass containsKey check
541539
assertThatCode {
@@ -586,8 +584,9 @@ class DefaultRailWayNetGridTest {
586584
Point(5, 5), Point(6, 6)
587585
)
588586
val parts = intermediatePoints.map { createTestTrackBlockPart() }
589-
val map = intermediatePoints.zip(parts).toMap()
590-
grid.putMap(map)
587+
val map = java.util.HashMap<Point, TrackBlockPart>()
588+
intermediatePoints.zip(parts).forEach { (point, part) -> map[point] = part }
589+
grid.putMap(map as java.util.Map<Point, TrackBlockPart>)
591590

592591
// Act - remove all intermediate cells (simulates removeLine scenario)
593592
grid.keySet().removeAll(intermediatePoints.toSet())

0 commit comments

Comments
 (0)