Skip to content

Commit 1665ca9

Browse files
bedaHovorkaclaude
andcommitted
Extract domain-specific Cell utilities from Util to CellUtilities
Refactors utility organization by separating domain model concerns: - Creates CellUtilities object for Cell-specific operations (assertNodeCell, toClass) - Keeps Util focused on type-agnostic utilities (assertInstanceOf) - Updates 4 files to use CellUtilities for domain operations - Simplifies RailwayNetGridCanvas context handling - Improves code organization and separation of concerns Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 014231d commit 1665ca9

7 files changed

Lines changed: 102 additions & 57 deletions

File tree

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ import cz.vutbr.fit.interlockSim.exceptions.requireValidState
3535
import cz.vutbr.fit.interlockSim.objects.cells.anti
3636
import cz.vutbr.fit.interlockSim.objects.cells.conflict
3737
import cz.vutbr.fit.interlockSim.objects.cells.segmentFor
38+
import cz.vutbr.fit.interlockSim.objects.cells.CellUtilities
3839
import cz.vutbr.fit.interlockSim.util.ExtendedUnorientedGraph
3940
import cz.vutbr.fit.interlockSim.util.HashMapGraph
4041
import cz.vutbr.fit.interlockSim.util.Point
42+
import cz.vutbr.fit.interlockSim.util.Util
4143
import cz.vutbr.fit.interlockSim.util.putMulti
4244
import cz.vutbr.fit.interlockSim.util.valuesMulti
43-
import cz.vutbr.fit.interlockSim.util.Util
4445
import io.github.oshai.kotlinlogging.KotlinLogging
4546
import jDisco.DiscoException
4647
import jDisco.Process
@@ -239,8 +240,8 @@ abstract class DefaultContext :
239240

240241
if (key1.distance(key2) <= SQRT2) return null
241242

242-
val nodecell1: NodeCell = Util.assertNodeCell(getGrid().get(key1)!!)
243-
val nodecell2: NodeCell = Util.assertNodeCell(getGrid().get(key2)!!)
243+
val nodecell1: NodeCell = CellUtilities.assertNodeCell(getGrid().get(key1)!!)
244+
val nodecell2: NodeCell = CellUtilities.assertNodeCell(getGrid().get(key2)!!)
244245

245246
for (s1: Segment in nodecell1.joins()) {
246247
val p1 = s1.transform(key1)
@@ -665,7 +666,7 @@ abstract class DefaultContext :
665666
// Match Java 1:1: return directly (inner method should not return null here)
666667
getSegment(separator, section) ?: throw IllegalStateException("getSegment returned null for TrackSection")
667668
} else {
668-
val nodeCell: NodeCell = Util.assertNodeCell(separator)
669+
val nodeCell: NodeCell = CellUtilities.assertNodeCell(separator)
669670
val trackBlock: TrackBlock = Util.assertInstanceOf(TrackBlock::class.java, track)
670671
// Match Java 1:1: return directly (inner method should not return null here)
671672
getSegment(nodeCell, trackBlock as TrackBlock?)
@@ -683,7 +684,7 @@ abstract class DefaultContext :
683684
if (trackBlock.isInnerElement(separator)) {
684685
return trackBlock.getJoin(separator, section)
685686
}
686-
val nodeCell: NodeCell = Util.assertNodeCell(separator)
687+
val nodeCell: NodeCell = CellUtilities.assertNodeCell(separator)
687688
return getSegment(nodeCell, trackBlock as TrackBlock?)
688689
}
689690

@@ -759,7 +760,7 @@ abstract class DefaultContext :
759760
}
760761

761762
// z dalsi TrackBlock
762-
val nodeCell = Util.assertNodeCell(separator)
763+
val nodeCell = CellUtilities.assertNodeCell(separator)
763764
val nextTrackBlock = getNextTrackBlock(nodeCell, trackBlock)
764765

765766
@Suppress("UNCHECKED_CAST")

src/main/kotlin/cz/vutbr/fit/interlockSim/gui/RailwayNetGridCanvas.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ package cz.vutbr.fit.interlockSim.gui
1212
import cz.vutbr.fit.interlockSim.context.Context
1313
import cz.vutbr.fit.interlockSim.context.EditingContext
1414
import cz.vutbr.fit.interlockSim.context.EditingContextFactory
15-
import cz.vutbr.fit.interlockSim.context.SimulationContext
1615
import cz.vutbr.fit.interlockSim.exceptions.requireEditor
1716
import cz.vutbr.fit.interlockSim.gui.gridcanvas.CellRenderer
1817
import cz.vutbr.fit.interlockSim.gui.gridcanvas.EditorCellRenderer
@@ -202,11 +201,11 @@ class RailwayNetGridCanvas :
202201
state = State.EDITING
203202
changeListeners(simulationControlListener, editListener)
204203
}
205-
is SimulationContext -> {
204+
else -> {
205+
// Covers SimulationContext and any future context types
206206
state = State.SIMULATION
207207
changeListeners(editListener, simulationControlListener)
208208
}
209-
else -> error("Unknown context type: ${newContext.javaClass}")
210209
}
211210
changeContext(newContext)
212211
}
@@ -333,8 +332,8 @@ class RailwayNetGridCanvas :
333332
}
334333

335334
@Deprecated("Use setContext instead")
336-
fun setSimulation(simulationContext: SimulationContext) {
337-
setContext(simulationContext)
335+
fun setSimulation(context: Context) {
336+
setContext(context)
338337
}
339338

340339
// Scrollable interface implementation
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* Brno University of Technology
2+
* Faculty of Information Technology
3+
*
4+
* BSc Thesis 2006/2007
5+
*
6+
* Railway Interlocking Simulator
7+
*
8+
* Bedrich Hovorka
9+
*/
10+
package cz.vutbr.fit.interlockSim.objects.cells
11+
12+
import cz.vutbr.fit.interlockSim.context.SimulationContext
13+
import cz.vutbr.fit.interlockSim.exceptions.requireValidState
14+
15+
/**
16+
* Domain-specific utility functions for working with Cell objects.
17+
*
18+
* This object contains utilities that depend on domain model classes (NodeCell, SimulationContext).
19+
* For type-agnostic utilities, see [cz.vutbr.fit.interlockSim.util.Util].
20+
*/
21+
object CellUtilities {
22+
/**
23+
* Most used cast in program - asserts and casts to NodeCell
24+
* @param obj object to cast
25+
* @return casted NodeCell instance
26+
* @throws IllegalStateException if obj is not a NodeCell
27+
*/
28+
fun assertNodeCell(obj: Any): NodeCell {
29+
requireValidState(obj is NodeCell) { "Expected instance of NodeCell but got ${obj.javaClass.name}: $obj" }
30+
return obj as NodeCell
31+
}
32+
33+
/**
34+
* Converts object to its class, with special handling for SimulationContext.
35+
*
36+
* This method is designed specifically for InterlockSim's reflection needs.
37+
* SimulationContext instances are always represented by the SimulationContext interface class,
38+
* not their concrete implementation class.
39+
*
40+
* @param o object to convert
41+
* @return Class representing the object's type
42+
*/
43+
fun toClass(o: Any): Class<*> {
44+
val class1 = o.javaClass
45+
return if (SimulationContext::class.java.isAssignableFrom(class1)) SimulationContext::class.java else class1
46+
}
47+
48+
/**
49+
* Converts array of objects to array of classes, with special handling for domain types.
50+
*
51+
* This method is designed specifically for InterlockSim's reflection needs.
52+
* Uses [toClass] for individual object conversion, which applies special handling for SimulationContext.
53+
*
54+
* @param objects array of objects (null values allowed)
55+
* @return array of classes representing types of objects (null for null inputs)
56+
* @throws IllegalStateException if any object is an array (arrays not supported)
57+
*/
58+
fun toClass(objects: Array<out Any?>): Array<Class<*>?> {
59+
val classes = arrayOfNulls<Class<*>>(objects.size)
60+
for (i in objects.indices) {
61+
val obj = objects[i]
62+
if (obj != null) {
63+
requireValidState(!obj.javaClass.isArray) { "Arrays are not supported as input objects" }
64+
classes[i] = toClass(obj)
65+
}
66+
}
67+
return classes
68+
}
69+
}

src/main/kotlin/cz/vutbr/fit/interlockSim/util/AbstractUnorientedGraph.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package cz.vutbr.fit.interlockSim.util
1111

1212
import cz.vutbr.fit.interlockSim.exceptions.requireValidState
13+
import cz.vutbr.fit.interlockSim.objects.cells.CellUtilities
1314
import java.lang.reflect.InvocationTargetException
1415
import java.util.Collection
1516
import java.util.Map
@@ -48,7 +49,7 @@ abstract class AbstractUnorientedGraph<N, E> : UnorientedGraph<N, E> {
4849
val methodName = e.methodName
4950
try {
5051
@Suppress("UNCHECKED_CAST")
51-
val classArray = Util.toClass(args) as Array<Class<*>?>
52+
val classArray = CellUtilities.toClass(args) as Array<Class<*>?>
5253
val method = o.javaClass.getMethod(methodName, *classArray)
5354
return method.invoke(o, *args)
5455
} catch (ee: InvocationTargetException) {

src/main/kotlin/cz/vutbr/fit/interlockSim/util/Util.kt

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,50 +9,23 @@
99
*/
1010
package cz.vutbr.fit.interlockSim.util
1111

12-
import cz.vutbr.fit.interlockSim.context.SimulationContext
1312
import cz.vutbr.fit.interlockSim.exceptions.requireValidState
14-
import cz.vutbr.fit.interlockSim.objects.cells.NodeCell
1513

1614
/**
17-
* Collection of shared static methods
15+
* Collection of type-agnostic utility methods.
1816
*
17+
* For domain-specific utilities (e.g., working with Cell objects),
18+
* see [cz.vutbr.fit.interlockSim.objects.cells.CellUtilities].
1919
*/
2020
object Util {
21-
private fun toClass(o: Any): Class<*> {
22-
val class1 = o.javaClass
23-
return if (SimulationContext::class.java.isAssignableFrom(class1)) SimulationContext::class.java else class1
24-
}
25-
26-
/**
27-
* This method is designed specifically for InterlockSim
28-
* @param objects array of objects
29-
* @return array of classes which represent types of objects
30-
*/
31-
fun toClass(objects: Array<out Any?>): Array<Class<*>?> {
32-
val classes = arrayOfNulls<Class<*>>(objects.size)
33-
for (i in objects.indices) {
34-
val obj = objects[i]
35-
if (obj != null) {
36-
requireValidState(!obj.javaClass.isArray) { "Arrays are not supported as input objects" }
37-
classes[i] = toClass(obj)
38-
}
39-
}
40-
return classes
41-
}
42-
43-
/**
44-
* Most used cast in program
45-
* @param obj
46-
* @return casted instance
47-
*/
48-
fun assertNodeCell(obj: Any): NodeCell = assertInstanceOf(NodeCell::class.java, obj)
49-
5021
/**
51-
* assert and cast routine
52-
* @param <T>
53-
* @param clazz
54-
* @param obj
55-
* @return casted instance
22+
* Generic type assertion and cast utility.
23+
*
24+
* @param T target type
25+
* @param clazz target class to cast to
26+
* @param obj object to cast
27+
* @return casted instance of type T
28+
* @throws IllegalStateException if obj is not an instance of clazz
5629
*/
5730
fun <T> assertInstanceOf(
5831
clazz: Class<T>,

src/main/kotlin/cz/vutbr/fit/interlockSim/xml/XMLContextFactory.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import cz.vutbr.fit.interlockSim.objects.paths.OrientedPathSeparator
2828
import cz.vutbr.fit.interlockSim.objects.paths.PathElement
2929
import cz.vutbr.fit.interlockSim.objects.tracks.SimpleTrackBlock
3030
import cz.vutbr.fit.interlockSim.objects.tracks.TrackBlock
31+
import cz.vutbr.fit.interlockSim.objects.cells.CellUtilities
3132
import cz.vutbr.fit.interlockSim.util.Doubleton
3233
import cz.vutbr.fit.interlockSim.util.Point
3334
import cz.vutbr.fit.interlockSim.util.Util
@@ -149,8 +150,8 @@ class XMLContextFactory :
149150
check(from != to) { "from and to points must be different" }
150151

151152
val railwayNetGrid = ctx.getRailWayNetGrid()
152-
val fromNode = Util.assertNodeCell(railwayNetGrid[from] as Any)
153-
val toNode = Util.assertNodeCell(railwayNetGrid[to] as Any)
153+
val fromNode = CellUtilities.assertNodeCell(railwayNetGrid[from] as Any)
154+
val toNode = CellUtilities.assertNodeCell(railwayNetGrid[to] as Any)
154155

155156
val segmentFrom =
156157
getEnum(uri, attributes, Segment::class.java, FROM)

src/test/kotlin/cz/vutbr/fit/interlockSim/util/UtilTest.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cz.vutbr.fit.interlockSim.util
22

33
import cz.vutbr.fit.interlockSim.objects.cells.Cell
4+
import cz.vutbr.fit.interlockSim.objects.cells.CellUtilities
45
import cz.vutbr.fit.interlockSim.objects.cells.NodeCell
56
import assertk.assertThat
67
import assertk.assertions.isEqualTo
@@ -15,7 +16,7 @@ class UtilTest {
1516
@Test
1617
fun `toClass array returns actual class for non-SimulationContext objects`() {
1718
val dummy = RailSemaphore(false, Cell.SpatialType.HORIZONTAL)
18-
val result = Util.toClass(arrayOf(dummy))
19+
val result = CellUtilities.toClass(arrayOf(dummy))
1920
assertThat(result.size).isEqualTo(1)
2021
assertThat(result[0]).isEqualTo(RailSemaphore::class.java)
2122
}
@@ -27,7 +28,7 @@ class UtilTest {
2728
val obj3 = 42
2829
val objects = arrayOf(obj1, obj2, obj3)
2930

30-
val result = Util.toClass(objects)
31+
val result = CellUtilities.toClass(objects)
3132

3233
assertThat(result.size).isEqualTo(3)
3334
assertThat(result[0]).isEqualTo(RailSemaphore::class.java)
@@ -40,7 +41,7 @@ class UtilTest {
4041
val obj1 = RailSemaphore(false, Cell.SpatialType.HORIZONTAL)
4142
val objects = arrayOf<Any?>(obj1, null, "test")
4243

43-
val result = Util.toClass(objects)
44+
val result = CellUtilities.toClass(objects)
4445

4546
assertThat(result.size).isEqualTo(3)
4647
assertThat(result[0]).isEqualTo(RailSemaphore::class.java)
@@ -51,21 +52,21 @@ class UtilTest {
5152
@Test
5253
fun `toClass array returns empty array for empty input`() {
5354
val objects = emptyArray<Any>()
54-
val result = Util.toClass(objects)
55+
val result = CellUtilities.toClass(objects)
5556
assertThat(result.size).isEqualTo(0)
5657
}
5758

5859
@Test
5960
fun `assertNodeCell returns NodeCell when correct type`() {
6061
val dummy = RailSemaphore(false, Cell.SpatialType.HORIZONTAL)
61-
val result = Util.assertNodeCell(dummy)
62+
val result = CellUtilities.assertNodeCell(dummy)
6263
assertThat(result).isSameInstanceAs(dummy)
6364
}
6465

6566
@Test
6667
fun `assertNodeCell throws AssertionError for wrong type`() {
6768
val dummy = "String"
68-
assertThrows<IllegalStateException> { Util.assertNodeCell(dummy) }
69+
assertThrows<IllegalStateException> { CellUtilities.assertNodeCell(dummy) }
6970
}
7071

7172
@Test

0 commit comments

Comments
 (0)