Skip to content

Commit

Permalink
Added state-transition table representation and support for CFG (#13)
Browse files Browse the repository at this point in the history
* Added prototype

* Temp UI fix and now tests should be fine

* Added unit tests and made examples' description better

* Trying to fix tests

* Just testing

* Just checking out

* Added state-transition table representation and support for CFG

* Added adjacency matrix view

* Added base class for table views

* Trying to get renaming states handled properly

* Now renaming states should be handled properly

* Hellings algo, part 1

* Fixed bug with changing transitions filters

* Added prototype of Hellings algo implementation

* Improved overall appearance

* Fixed bug with opening an example

* Trying to pass tests

* Resolved almost all issues

* Resolved issues

* Small fixes

* Layout fix, part 1

* Trying to improve appearance

* Resolved all issues

* Tables are now bigger

* Fixed a bug with showing empty grammars

* Added input of CFG

* Improved appearance and updated localisation

* Small fixes

* Small fix of table layout and deleted unused imports

* Fixed tables resizing

* Small fixes

* Now Hellings is done for finite automatons

* Settings editor is now hideable from table views

* Resolved small issues with CFG

* Fixed bug with displaying a vertex multiple times and other small things

* Test panel UI issue + bug with opening empty tests

* Small fixes + added test for Hellings algo

* Fixed bugs with Hellings algo

* Fixed notifying when Hellings algo grammar window has at least 1 blank field

* Fixed some bugs in conversion to CFG and added more tests

* Fixed some UI issues and now conversion to CFG is affected by automaton changes

* Fixed bug with conversion to CFG + removed scrolling for table representations

* Revert "Fixed bug with conversion to CFG + removed scrolling for table representations"

This reverts commit 1056d05.

* Fixed all known issues

* Fixed bug with displaying initial and final vertices in tables + now its possible to delete productions from Hellings grammar

* Definitive tests fix

* Removed 2 abundant files

* Made tables resizing better

* Small fix

* Added files for the new wiki

* Deleted useless file

* Added AutomatonTableVertexView
  • Loading branch information
IgorFilimonov authored Oct 7, 2024
1 parent 14f51db commit 059f742
Show file tree
Hide file tree
Showing 48 changed files with 3,207 additions and 143 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package automaton.constructor.controller

import automaton.constructor.controller.algorithms.ConversionToCFGController
import automaton.constructor.controller.algorithms.HellingsAlgoController
import automaton.constructor.model.automaton.Automaton
import automaton.constructor.model.automaton.FiniteAutomaton
import automaton.constructor.model.automaton.PushdownAutomaton
import automaton.constructor.utils.I18N
import tornadofx.Controller

class AlgorithmsController(
private val openedAutomaton: Automaton
): Controller() {
fun convertToCFG() {
if (openedAutomaton !is PushdownAutomaton || openedAutomaton.stacks.size > 1) {
tornadofx.error(I18N.messages.getString("CFGView.Error"))
return
}
ConversionToCFGController(openedAutomaton).convertToCFG()
}

fun executeHellingsAlgo() {
if (openedAutomaton !is FiniteAutomaton) {
tornadofx.error(I18N.messages.getString("HellingsAlgorithm.Error"))
return
}
HellingsAlgoController(openedAutomaton).getGrammar()
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package automaton.constructor.controller

import automaton.constructor.model.action.Action
import automaton.constructor.model.action.ActionAvailability
import automaton.constructor.model.action.ActionFailedException
import automaton.constructor.model.automaton.Automaton
import automaton.constructor.model.automaton.allowsBuildingBlocks
import automaton.constructor.model.data.addContent
import automaton.constructor.model.element.*
import automaton.constructor.utils.*
import automaton.constructor.view.*
import automaton.constructor.view.automaton.AutomatonGraphView
import javafx.geometry.Point2D
import javafx.scene.control.ContextMenu
import javafx.scene.input.KeyCode
import javafx.scene.input.MouseButton
import javafx.scene.shape.Line
import tornadofx.*

class AutomatonGraphController(val automaton: Automaton, val automatonViewContext: AutomatonViewContext) :
Controller() {
class AutomatonGraphController(automaton: Automaton, automatonViewContext: AutomatonViewContext) :
AutomatonRepresentationController(automaton, automatonViewContext) {
private val settingsController by inject<SettingsController>()
private val newTransitionLine = Line().apply { isVisible = false }
private var newTransitionSourceProperty = objectProperty<AutomatonVertexView?>(null).apply {
Expand All @@ -32,9 +30,6 @@ class AutomatonGraphController(val automaton: Automaton, val automatonViewContex
}
}
private var newTransitionSource by newTransitionSourceProperty
val lastSelectedElementProperty = objectProperty<AutomatonElementView?>(null)
var lastSelectedElement by lastSelectedElementProperty
private val selectedElementsViews = mutableSetOf<AutomatonElementView>()

fun select(elements: Set<AutomatonElementView>) {
clearSelection()
Expand Down Expand Up @@ -108,9 +103,11 @@ class AutomatonGraphController(val automaton: Automaton, val automatonViewContex
clearSelection()
} else if (event.code == KeyCode.A && event.isControlDown) {
clearSelection()
selectedElementsViews.addAll(graphView.edgeViews.values.flatMap { it.transitionViews }
selectedElementsViews.addAll(
graphView.edgeViews.values.flatMap { it.transitionViews }
.onEach { it.selected = true })
selectedElementsViews.addAll(graphView.vertexToViewMap.values.onEach { it.selected = true })
selectedElementsViews.addAll(
graphView.vertexToViewMap.values.onEach { it.selected = true })
}
}
}
Expand Down Expand Up @@ -196,95 +193,6 @@ class AutomatonGraphController(val automaton: Automaton, val automatonViewContex
}
}

private fun registerTransitionView(transitionView: TransitionView) = registerAutomatonElementView(transitionView)

private fun registerAutomatonElementView(automatonElementView: AutomatonElementView) {
automatonElementView.setOnMouseClicked {
it.consume()
automatonElementView.requestFocus()
if (it.button == MouseButton.PRIMARY) {
if (it.isStillSincePress)
automaton.isOutputOfTransformation?.let { transformation ->
transformation.step(automatonElementView.automatonElement)
return@setOnMouseClicked
}
lastSelectedElement = when {
!it.isControlDown -> {
clearSelection()
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
automatonElementView
}
automatonElementView.selected -> {
selectedElementsViews.remove(automatonElementView)
automatonElementView.selected = false
null
}
else -> {
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
automatonElementView
}
}
} else if (it.button == MouseButton.SECONDARY && it.isStillSincePress && automaton.allowsModificationsByUser) {
fun <T : AutomatonElement> showActionsMenu(element: T, actions: List<Action<T>>) {
val actionsWithAvailability = actions.map { action ->
action to action.getAvailabilityFor(element)
}

if (actionsWithAvailability.any { (_, availability) -> availability != ActionAvailability.HIDDEN }) {
ContextMenu().apply {
for ((action, availability) in actionsWithAvailability) {
item(action.displayName, action.keyCombination) {
action {
try {
if (automaton.allowsModificationsByUser)
action.performOn(element)
} catch (exc: ActionFailedException) {
error(
exc.message,
title = I18N.messages.getString("Dialog.error"),
owner = automatonViewContext.uiComponent.currentWindow
)
}

}
isVisible = availability != ActionAvailability.HIDDEN
isDisable = availability == ActionAvailability.DISABLED
}
}
show(automatonElementView.scene.window, it.screenX, it.screenY)
}
clearSelection()
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
lastSelectedElement = automatonElementView
}
}
when (automatonElementView.automatonElement) {
is State -> showActionsMenu(
automatonElementView.automatonElement,
automaton.stateActions
)
is BuildingBlock -> showActionsMenu(
automatonElementView.automatonElement,
automaton.buildingBlockActions
)
is Transition -> showActionsMenu(
automatonElementView.automatonElement,
automaton.transitionActions
)
}
}
}
clearSelection()
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
lastSelectedElement = automatonElementView
}

fun clearSelection() {
selectedElementsViews.onEach { it.selected = false }.clear()
lastSelectedElement = null
}
private fun registerTransitionView(transitionView: TransitionView) =
registerAutomatonElementView(transitionView)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package automaton.constructor.controller

import automaton.constructor.model.action.Action
import automaton.constructor.model.action.ActionAvailability
import automaton.constructor.model.action.ActionFailedException
import automaton.constructor.model.automaton.Automaton
import automaton.constructor.model.element.AutomatonElement
import automaton.constructor.model.element.BuildingBlock
import automaton.constructor.model.element.State
import automaton.constructor.model.element.Transition
import automaton.constructor.utils.I18N
import automaton.constructor.view.AutomatonElementView
import automaton.constructor.view.AutomatonViewContext
import javafx.scene.control.ContextMenu
import javafx.scene.input.MouseButton
import tornadofx.*

open class AutomatonRepresentationController(
val automaton: Automaton,
val automatonViewContext: AutomatonViewContext
): Controller() {
val lastSelectedElementProperty = objectProperty<AutomatonElementView?>(null).also {
it.addListener { _, _, newValue ->
if (newValue == null) {
clearSelection()
}
}
}
var lastSelectedElement by lastSelectedElementProperty
val selectedElementsViews = mutableSetOf<AutomatonElementView>()

fun registerAutomatonElementView(automatonElementView: AutomatonElementView) {
automatonElementView.setOnMouseClicked {
it.consume()
automatonElementView.requestFocus()
if (it.button == MouseButton.PRIMARY) {
if (it.isStillSincePress)
automaton.isOutputOfTransformation?.let { transformation ->
transformation.step(automatonElementView.automatonElement)
return@setOnMouseClicked
}
lastSelectedElement = when {
!it.isControlDown -> {
clearSelection()
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
automatonElementView
}
automatonElementView.selected -> {
selectedElementsViews.remove(automatonElementView)
automatonElementView.selected = false
null
}
else -> {
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
automatonElementView
}
}
} else if (it.button == MouseButton.SECONDARY && it.isStillSincePress && automaton.allowsModificationsByUser) {
fun <T : AutomatonElement> showActionsMenu(element: T, actions: List<Action<T>>) {
val actionsWithAvailability = actions.map { action ->
action to action.getAvailabilityFor(element)
}

if (actionsWithAvailability.any { (_, availability) -> availability != ActionAvailability.HIDDEN }) {
ContextMenu().apply {
for ((action, availability) in actionsWithAvailability) {
item(action.displayName, action.keyCombination) {
action {
try {
if (automaton.allowsModificationsByUser)
action.performOn(element)
} catch (exc: ActionFailedException) {
error(
exc.message,
title = I18N.messages.getString("Dialog.error"),
owner = automatonViewContext.uiComponent.currentWindow
)
}

}
isVisible = availability != ActionAvailability.HIDDEN
isDisable = availability == ActionAvailability.DISABLED
}
}
show(automatonElementView.scene.window, it.screenX, it.screenY)
}
clearSelection()
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
lastSelectedElement = automatonElementView
}
}
when (automatonElementView.automatonElement) {
is State -> showActionsMenu(
automatonElementView.automatonElement,
automaton.stateActions
)
is BuildingBlock -> showActionsMenu(
automatonElementView.automatonElement,
automaton.buildingBlockActions
)
is Transition -> showActionsMenu(
automatonElementView.automatonElement,
automaton.transitionActions
)
}
}
}
clearSelection()
selectedElementsViews.add(automatonElementView)
automatonElementView.selected = true
lastSelectedElement = automatonElementView
}

fun clearSelection() {
selectedElementsViews.onEach { it.selected = false }.clear()
lastSelectedElement = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import automaton.constructor.model.module.hasRegexes
import automaton.constructor.model.serializers.AutomatonSerializer
import automaton.constructor.model.serializers.automatonSerializers
import automaton.constructor.utils.*
import automaton.constructor.view.TestsView
import automaton.constructor.view.tests.TestsView
import javafx.beans.binding.Binding
import javafx.concurrent.Task
import javafx.geometry.Pos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import automaton.constructor.model.memory.Test
import automaton.constructor.model.memory.TestsForSerializing
import automaton.constructor.utils.*
import automaton.constructor.utils.addOnSuccess
import automaton.constructor.view.TestAndResult
import automaton.constructor.view.TestsView
import automaton.constructor.view.TestsResultsFragment
import automaton.constructor.view.tests.TestAndResult
import automaton.constructor.view.tests.TestsView
import automaton.constructor.view.tests.TestsResultsFragment
import javafx.concurrent.Task
import javafx.scene.control.Alert
import javafx.scene.control.ButtonType
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package automaton.constructor.controller.algorithms

import automaton.constructor.model.automaton.PushdownAutomaton
import automaton.constructor.utils.I18N
import automaton.constructor.view.algorithms.CFGView
import tornadofx.Controller

class ConversionToCFGController(private val openedAutomaton: PushdownAutomaton): Controller() {
fun convertToCFG() {
val conversionToCFGWindow = find<CFGView>(mapOf(
CFGView::grammar to openedAutomaton.convertToCFG()
))
conversionToCFGWindow.title = I18N.messages.getString("CFGView.Title")
conversionToCFGWindow.openWindow()
}
}
Loading

0 comments on commit 059f742

Please sign in to comment.