Skip to content

Commit

Permalink
FBP-56. Fixed problem with cascading
Browse files Browse the repository at this point in the history
  • Loading branch information
valb3r committed May 25, 2020
1 parent 1ffcae5 commit 730c2e4
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class ProcessModelUpdateEvents(private val parser: BpmnParser, private val proje
private var expectedFileHash: String = ""

private val fileCommitListeners: MutableList<Any> = ArrayList()
private val broadcastPropertyEvents: MutableList<Order<PropertyUpdateWithId>> = ArrayList()
private val parentCreatesByStaticId: MutableMap<DiagramElementId, MutableList<Order<out Event>>> = HashMap()
private val locationUpdatesByStaticId: MutableMap<DiagramElementId, MutableList<Order<out Event>>> = HashMap()
private val propertyUpdatesByStaticId: MutableMap<BpmnElementId, MutableList<Order<out Event>>> = HashMap()
Expand All @@ -57,7 +56,6 @@ class ProcessModelUpdateEvents(private val parser: BpmnParser, private val proje
newDiagramElements.clear()
deletionsByStaticId.clear()
deletionsByStaticBpmnId.clear()
broadcastPropertyEvents.clear()
expectedFileHash = hashData(fileContent)
baseFileContent = fileContent
}
Expand Down Expand Up @@ -122,11 +120,6 @@ class ProcessModelUpdateEvents(private val parser: BpmnParser, private val proje
allBeforeThis++
updates.add(toStore)
propertyUpdatesByStaticId.computeIfAbsent(event.bpmnElementId) { CopyOnWriteArrayList() } += toStore

if (event.property.cascades) {
broadcastPropertyEvents.add(toStore)
}

commitToFile()
}

Expand Down Expand Up @@ -187,20 +180,6 @@ class ProcessModelUpdateEvents(private val parser: BpmnParser, private val proje
commitToFile()
}

@Synchronized
fun currentPropertyUpdateEventListWithCascaded(elementId: BpmnElementId): List<EventOrder<PropertyUpdateWithId>> {
val cursorValue = allBeforeThis
val latestRemoval = lastDeletion(elementId)
val allEvents = propertyUpdatesByStaticId
.getOrDefault(elementId, emptyList<Order<PropertyUpdateWithId>>())
.filterIsInstance<Order<PropertyUpdateWithId>>()
.filter { it.order < cursorValue }
.toMutableList()
allEvents.addAll(broadcastPropertyEvents.filter { it.order < cursorValue })

return allEvents.filter { it.order > latestRemoval.order}
}

@Synchronized
fun currentPropertyUpdateEventList(elementId: BpmnElementId): List<EventOrder<PropertyUpdateWithId>> {
val cursorValue = allBeforeThis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import com.valb3r.bpmn.intellij.plugin.events.BooleanValueUpdatedEvent
import com.valb3r.bpmn.intellij.plugin.events.StringValueUpdatedEvent
import com.valb3r.bpmn.intellij.plugin.events.updateEventsRegistry
import com.valb3r.bpmn.intellij.plugin.ui.components.FirstColumnReadOnlyModel
import java.util.concurrent.atomic.AtomicReference
import javax.swing.JTable

class PropertiesVisualizer(val table: JTable, val editorFactory: (value: String) -> EditorTextField) {
Expand All @@ -36,7 +35,7 @@ class PropertiesVisualizer(val table: JTable, val editorFactory: (value: String)
}

@Synchronized
fun visualize(bpmnElementId: BpmnElementId, properties: Map<PropertyType, Property>) {
fun visualize(state: Map<BpmnElementId, Map<PropertyType, Property>>, bpmnElementId: BpmnElementId, properties: Map<PropertyType, Property>) {
notifyDeFocusElement()

// drop and re-create table model
Expand All @@ -48,10 +47,10 @@ class PropertiesVisualizer(val table: JTable, val editorFactory: (value: String)

properties.forEach {
when(it.key.valueType) {
STRING -> model.addRow(arrayOf(it.key.caption, buildTextField(bpmnElementId, it.key, it.value)))
STRING -> model.addRow(arrayOf(it.key.caption, buildTextField(state, bpmnElementId, it.key, it.value)))
BOOLEAN -> model.addRow(arrayOf(it.key.caption, buildCheckboxField(bpmnElementId, it.key, it.value)))
CLASS -> model.addRow(arrayOf(it.key.caption, buildClassField(bpmnElementId, it.key, it.value)))
EXPRESSION -> model.addRow(arrayOf(it.key.caption, buildExpressionField(bpmnElementId, it.key, it.value)))
CLASS -> model.addRow(arrayOf(it.key.caption, buildClassField(state, bpmnElementId, it.key, it.value)))
EXPRESSION -> model.addRow(arrayOf(it.key.caption, buildExpressionField(state, bpmnElementId, it.key, it.value)))
}
}
model.fireTableDataChanged()
Expand All @@ -63,14 +62,15 @@ class PropertiesVisualizer(val table: JTable, val editorFactory: (value: String)
listenersForCurrentView.clear()
}

private fun buildTextField(bpmnElementId: BpmnElementId, type: PropertyType, value: Property): JBTextField {
val fieldValue = lastStringValueFromRegistry(bpmnElementId, type, value.value as String?) ?: (value.value as String? ?: "")
private fun buildTextField(state: Map<BpmnElementId, Map<PropertyType, Property>>, bpmnElementId: BpmnElementId, type: PropertyType, value: Property): JBTextField {
val fieldValue = lastStringValueFromRegistry(bpmnElementId, type) ?: (value.value as String? ?: "")
val field = JBTextField(fieldValue)
val initialValue = field.text

listenersForCurrentView.computeIfAbsent(type.updateOrder) { mutableListOf()}.add {
if (initialValue != field.text) {
updateEventsRegistry().addPropertyUpdateEvent(
emitStringUpdateWithCascadeIfNeeded(
state,
StringValueUpdatedEvent(
bpmnElementId,
type,
Expand All @@ -97,40 +97,51 @@ class PropertiesVisualizer(val table: JTable, val editorFactory: (value: String)
return field
}

private fun buildClassField(bpmnElementId: BpmnElementId, type: PropertyType, value: Property): EditorTextField {
val fieldValue = lastStringValueFromRegistry(bpmnElementId, type, value.value as String?) ?: (value.value as String? ?: "")
private fun buildClassField(state: Map<BpmnElementId, Map<PropertyType, Property>>, bpmnElementId: BpmnElementId, type: PropertyType, value: Property): EditorTextField {
val fieldValue = lastStringValueFromRegistry(bpmnElementId, type) ?: (value.value as String? ?: "")
val field = editorFactory(fieldValue)
addEditorTextListener(field, bpmnElementId, type)
addEditorTextListener(state, field, bpmnElementId, type)
return field
}

private fun buildExpressionField(bpmnElementId: BpmnElementId, type: PropertyType, value: Property): EditorTextField {
val fieldValue = lastStringValueFromRegistry(bpmnElementId, type, value.value as String?) ?: (value.value as String? ?: "")
private fun buildExpressionField(state: Map<BpmnElementId, Map<PropertyType, Property>>, bpmnElementId: BpmnElementId, type: PropertyType, value: Property): EditorTextField {
val fieldValue = lastStringValueFromRegistry(bpmnElementId, type) ?: (value.value as String? ?: "")
val field = editorFactory( "\"${fieldValue}\"")
addEditorTextListener(field, bpmnElementId, type)
addEditorTextListener(state, field, bpmnElementId, type)
return field
}

private fun addEditorTextListener(field: EditorTextField, bpmnElementId: BpmnElementId, type: PropertyType) {
private fun addEditorTextListener(state: Map<BpmnElementId, Map<PropertyType, Property>>, field: EditorTextField, bpmnElementId: BpmnElementId, type: PropertyType) {
val initialValue = field.text
listenersForCurrentView.computeIfAbsent(type.updateOrder) { mutableListOf()}.add {
if (initialValue != field.text) {
updateEventsRegistry().addPropertyUpdateEvent(StringValueUpdatedEvent(bpmnElementId, type, removeQuotes(field.text)))
emitStringUpdateWithCascadeIfNeeded(state, StringValueUpdatedEvent(bpmnElementId, type, removeQuotes(field.text)))
}
}
}

private fun emitStringUpdateWithCascadeIfNeeded(state: Map<BpmnElementId, Map<PropertyType, Property>>, event: StringValueUpdatedEvent) {
val cascades = mutableListOf<PropertyUpdateWithId>()
if (null != event.referencedValue) {
state.forEach { id, props ->
props.filter { it.key.updatedBy == event.property }.filter { it.value.value == event.referencedValue }.forEach {prop ->
cascades += StringValueUpdatedEvent(id, prop.key, event.newValue, event.referencedValue, null)
}
}
}

updateEventsRegistry().addEvents(listOf(event) + cascades)
}

private fun removeQuotes(value: String): String {
return value.replace("^\"".toRegex(), "").replace("\"$".toRegex(), "")
}

private fun lastStringValueFromRegistry(bpmnElementId: BpmnElementId, type: PropertyType, currentValue: String?): String? {
val computedValue = AtomicReference<String>(currentValue)
return (updateEventsRegistry().currentPropertyUpdateEventListWithCascaded(bpmnElementId)
private fun lastStringValueFromRegistry(bpmnElementId: BpmnElementId, type: PropertyType): String? {
return (updateEventsRegistry().currentPropertyUpdateEventList(bpmnElementId)
.map { it.event }
.filter {
checkCascadedApplied(computedValue, bpmnElementId, type, it)
|| (bpmnElementId == it.bpmnElementId && it.property.id == type.id)
bpmnElementId == it.bpmnElementId && it.property.id == type.id
}
.lastOrNull { it is StringValueUpdatedEvent } as StringValueUpdatedEvent?)
?.newValue
Expand All @@ -144,21 +155,4 @@ class PropertiesVisualizer(val table: JTable, val editorFactory: (value: String)
.lastOrNull { it is BooleanValueUpdatedEvent } as BooleanValueUpdatedEvent?)
?.newValue
}

private fun checkCascadedApplied(computedValue: AtomicReference<String>, elementId: BpmnElementId, type: PropertyType, possibleCascade: PropertyUpdateWithId): Boolean {
if ((elementId == possibleCascade.bpmnElementId || possibleCascade.property == type) || null == possibleCascade.referencedValue) {
return false
}

if (!possibleCascade.property.cascades) {
return true
}

if (possibleCascade.property == type.updatedBy && possibleCascade.newValue is String && possibleCascade.referencedValue == computedValue.get()) {
computedValue.set(possibleCascade.newValue as String)
return true
}

return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class BpmnProcessRenderer {
state.elemPropertiesByStaticElementId.forEach { (owner, props) ->
idCascadesTo.intersect(props.keys).filter { props[it]?.value == parent.id }.forEach { type ->
when (state.elementByBpmnId[owner]) {
is BpmnSequenceFlow -> result += computeCascadeToWaypoint(state, owner, type)
is BpmnSequenceFlow -> result += computeCascadeToWaypoint(state, parent, owner, type)
}
}

Expand All @@ -116,12 +116,12 @@ class BpmnProcessRenderer {
return result
}

private fun computeCascadeToWaypoint(state: CurrentState, owner: BpmnElementId, type: PropertyType): Collection<CascadeTranslationToWaypoint> {
private fun computeCascadeToWaypoint(state: CurrentState, cascadeTrigger: BpmnElementId, owner: BpmnElementId, type: PropertyType): Collection<CascadeTranslationToWaypoint> {
return state.edges
.filter { it.bpmnElement == owner }
.map {
val index = if (type == SOURCE_REF) 0 else it.waypoint.size - 1
CascadeTranslationToWaypoint(it.waypoint[index].id, it.id, it.waypoint[index].internalPhysicalPos)
CascadeTranslationToWaypoint(cascadeTrigger, it.waypoint[index].id, it.id, it.waypoint[index].internalPhysicalPos)
}
}

Expand Down Expand Up @@ -177,12 +177,13 @@ class BpmnProcessRenderer {
areaByElement += actionsElem
}

renderMeta.interactionContext.dragEndCallbacks[it.id] = OnDragEnd@{ dx: Float, dy: Float, droppedOn: BpmnElementId? ->
renderMeta.interactionContext.dragEndCallbacks[it.id] = OnDragEnd@{ dx: Float, dy: Float, _: BpmnElementId? ->
val events = mutableListOf<DraggedToEvent>()
events += DraggedToEvent(it.id, dx, dy, null, null)
events += renderMeta
.cascadedTransalationOf
.filter { !renderMeta.interactionContext.draggedIds.contains(it.waypointId) }
.filter { target -> target.cascadeSource == it.bpmnElement}
.filter { target -> !renderMeta.interactionContext.draggedIds.contains(target.waypointId) }
.map { cascadeTo -> DraggedToEvent(cascadeTo.waypointId, dx, dy, cascadeTo.parentEdgeId, cascadeTo.internalId) }
return@OnDragEnd events
}
Expand Down Expand Up @@ -248,7 +249,9 @@ class BpmnProcessRenderer {
val index = parent.waypoint.indexOf(elem)
if (elem.physical) {
events += DraggedToEvent(elem.id, dx, dy, parent.id, elem.internalPhysicalPos)
if (null != droppedOn && null != parent.bpmnElement) {
if (null != droppedOn && null != parent.bpmnElement
&& meta.interactionContext.draggedIds.containsAll(setOf(elem.id, parent.id))
&& meta.interactionContext.draggedIds.size == 2) {
if (parent.waypoint.size - 1 == index ) {
events += StringValueUpdatedEvent(parent.bpmnElement!!, TARGET_REF, droppedOn.id)
} else if (0 == index) {
Expand Down Expand Up @@ -531,7 +534,7 @@ class BpmnProcessRenderer {
val cascadedTransalationOf: Set<CascadeTranslationToWaypoint>
)

private data class CascadeTranslationToWaypoint(val waypointId: DiagramElementId, val parentEdgeId: DiagramElementId, val internalId: Int)
private data class CascadeTranslationToWaypoint(val cascadeSource: BpmnElementId, val waypointId: DiagramElementId, val parentEdgeId: DiagramElementId, val internalId: Int)

private enum class Actions {
DELETE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,11 @@ class Canvas: JPanel() {
repaint()

val elementIdForPropertiesTable = selectedElements.firstOrNull()
stateProvider.currentState()
val state = stateProvider.currentState()
state
.elementByDiagramId[elementIdForPropertiesTable]
?.let { elemId ->
stateProvider.currentState().elemPropertiesByStaticElementId[elemId]?.let { propertiesVisualizer?.visualize(elemId, it) }
state.elemPropertiesByStaticElementId[elemId]?.let { propertiesVisualizer?.visualize(state.elemPropertiesByStaticElementId, elemId, it) }
} ?: propertiesVisualizer?.clear()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.valb3r.bpmn.intellij.plugin.flowable.parser.nodes.BpmnFile
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import java.io.ByteArrayInputStream
import java.io.StringWriter
import java.io.Writer
Expand Down Expand Up @@ -290,10 +289,6 @@ class FlowableParser : BpmnParser {
}

private fun applyPropertyUpdateWithId(doc: Document, update: PropertyUpdateWithId) {
if (update.property.cascades) {
applyCascadedPropertyUpdateWithId(doc, update)
}

val xpath = xpathFactory.newXPath()
val node = xpath.evaluate(
"//process/*[@id='${update.bpmnElementId.id}'][1]",
Expand All @@ -316,28 +311,6 @@ class FlowableParser : BpmnParser {
diagramElement.setAttribute("bpmnElement", update.newIdValue!!.id)
}

private fun applyCascadedPropertyUpdateWithId(doc: Document, update: PropertyUpdateWithId) {
if (null == update.referencedValue) {
throw NullPointerException("Referenced value for cascaded is missing")
}

PropertyType.values()
.filter { it.updatedBy == update.property }
.forEach { type ->
val details = PropertyTypeDetails.values().firstOrNull { it.propertyType == type }!!
val xpath = xpathFactory.newXPath()
val nodes = xpath.evaluate(
"//process/*[@${details.xmlPath}='${update.referencedValue as String}']",
doc,
XPathConstants.NODESET
) as NodeList

for (pos in 0 until nodes.length) {
setToNode(doc, nodes.item(pos) as Element, details.propertyType, update.newValue)
}
}
}


private fun setToNode(doc: Document, node: Element, type: PropertyType, value: Any?) {
val details = PropertyTypeDetails.values().filter { it.propertyType == type }.firstOrNull()!!
Expand Down

0 comments on commit 730c2e4

Please sign in to comment.