Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
57.14% |
4 / 7 |
CRAP | |
76.03% |
111 / 146 |
GrafovatkoProcessor | |
0.00% |
0 / 1 |
|
57.14% |
4 / 7 |
114.96 | |
76.03% |
111 / 146 |
__construct | |
100.00% |
1 / 1 |
2 | |
100.00% |
2 / 2 |
|||
setPrefix | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
setTargetParticipant | |
100.00% |
1 / 1 |
2 | |
100.00% |
2 / 2 |
|||
processGraph | |
0.00% |
0 / 1 |
6.35 | |
78.57% |
11 / 14 |
|||
processNodeAttrs | |
0.00% |
0 / 1 |
59.92 | |
74.42% |
64 / 86 |
|||
processEdgeAttrs | |
0.00% |
0 / 1 |
16.34 | |
72.97% |
27 / 37 |
|||
getExtraSvgElements | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
<?php declare(strict_types = 1); | |
/* | |
* Copyright (c) 2019, Josef Kufner <josef@kufner.cz> | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
namespace Smalldb\StateMachine\BpmnExtension; | |
use Smalldb\Graph\Edge; | |
use Smalldb\Graph\Grafovatko\ProcessorInterface; | |
use Smalldb\Graph\Graph; | |
use Smalldb\Graph\NestedGraph; | |
use Smalldb\Graph\Node; | |
class GrafovatkoProcessor implements ProcessorInterface | |
{ | |
private string $prefix = ''; | |
private ?string $targetParticipant = null; | |
public function __construct(?string $targetParticipant = null) | |
{ | |
$this->targetParticipant = $targetParticipant; | |
} | |
public function setPrefix(string $prefix): void | |
{ | |
$this->prefix = $prefix; | |
} | |
public function setTargetParticipant(?string $targetParticipant): void | |
{ | |
$this->targetParticipant = $targetParticipant; | |
} | |
/** | |
* Returns modified $exportedGraph which become the graph's attributes. | |
*/ | |
public function processGraph(NestedGraph $graph, array $exportedGraph): array | |
{ | |
$parentNode = $graph->getParentNode(); | |
$parentNodeType = $parentNode ? $parentNode->getAttr('type') : null; | |
if (!$parentNode || $parentNodeType === 'bpmnDiagram') { | |
$exportedGraph['layout'] = 'column'; | |
$exportedGraph['layoutOptions'] = [ | |
'sortNodes' => true, | |
]; | |
} else if ($parentNodeType === 'participant') { | |
if ($parentNode->getAttr('_is_state_machine', false)) { | |
$exportedGraph['layout'] = 'row'; | |
$exportedGraph['layoutOptions'] = [ | |
'sortNodes' => 'attr', | |
'sortAttr' => '_distance', | |
'arcEdges' => false, | |
]; | |
} else { | |
$exportedGraph['layout'] = 'dagre'; | |
$exportedGraph['layoutOptions'] = [ | |
'rankdir' => 'LR', | |
]; | |
} | |
} | |
return $exportedGraph; | |
} | |
/** | |
* Returns modified $exportedNode which become the node's attributes. | |
*/ | |
public function processNodeAttrs(Node $node, array $exportedNode): array | |
{ | |
$exportedNode['fill'] = "#fff"; | |
// Node label | |
if (isset($node['name'])) { | |
$label = trim($node['name']); | |
if ($node['_generated'] && $label != '') { | |
$label = "($label)"; | |
} | |
$exportedNode['label'] = $label; | |
} | |
if ($node['type'] != 'participant') { | |
$exportedNode['tooltip'] = json_encode($node->getAttributes(), JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); | |
} | |
// Node type (symbol) | |
switch ($node['type']) { | |
case 'bpmnDiagram': | |
$exportedNode['color'] = "#5373B4"; | |
break; | |
case 'task': | |
case 'sendTask': | |
case 'receiveTask': | |
case 'userTask': | |
case 'serviceTask': | |
$exportedNode['shape'] = 'bpmn.task'; | |
break; | |
case 'participant': | |
if ($this->targetParticipant && $node->getId() === $this->targetParticipant) { | |
$exportedNode['fill'] = "#ddffbb"; | |
} | |
break; | |
case 'startEvent': | |
case 'intermediateCatchEvent': | |
case 'intermediateThrowEvent': | |
case 'endEvent': | |
$exportedNode['shape'] = 'bpmn.event'; | |
$exportedNode['event_type'] = ['startEvent' => 'start', 'endEvent' => 'end'][$node['type']] ?? 'intermediate'; | |
$exportedNode['event_is_throwing'] = ($node['type'] == 'intermediateThrowEvent'); | |
if (isset($node['features']['timerEventDefinition'])) { | |
$exportedNode['event_symbol'] = 'timer'; | |
} else { | |
if (isset($node['features']['messageEventDefinition'])) { | |
$exportedNode['event_symbol'] = 'message'; | |
} | |
} | |
break; | |
case 'exclusiveGateway': | |
$exportedNode['shape'] = 'bpmn.gateway'; | |
$exportedNode['gateway_type'] = 'exclusive'; | |
break; | |
case 'eventBasedGateway': | |
$exportedNode['shape'] = 'bpmn.gateway'; | |
$exportedNode['gateway_type'] = 'event'; | |
break; | |
case 'parallelEventBasedGateway': | |
$exportedNode['shape'] = 'bpmn.gateway'; | |
$exportedNode['gateway_type'] = 'parallel_event'; | |
break; | |
case 'inclusiveGateway': | |
$exportedNode['shape'] = 'bpmn.gateway'; | |
$exportedNode['gateway_type'] = 'inclusive'; | |
break; | |
case 'complexGateway': | |
$exportedNode['shape'] = 'bpmn.gateway'; | |
$exportedNode['gateway_type'] = 'complex'; | |
break; | |
case 'parallelGateway': | |
$exportedNode['shape'] = 'bpmn.gateway'; | |
$exportedNode['gateway_type'] = 'parallel'; | |
break; | |
case 'textAnnotation': | |
$exportedNode['shape'] = 'note'; | |
$exportedNode['label'] = $node['text']; | |
$exportedNode['color'] = '#aaaaaa'; | |
break; | |
case 'error': | |
$exportedNode['label'] = $node['label']; | |
$exportedNode['shape'] = 'rect'; | |
$exportedNode['color'] = '#ff0000'; | |
$exportedNode['fill'] = '#ffeeee'; | |
break; | |
} | |
// Low opacity for generated and removed nodes | |
if ($node['_generated'] || !empty($node['_unused'])) { | |
$exportedNode['opacity'] = '0.4'; | |
} | |
// Node color | |
if ($node['_transition']) { | |
$exportedNode['color'] = '#2266cc'; | |
} else if ($node['_state']) { | |
$exportedNode['color'] = '#66aa22'; | |
} | |
// Receiving/invoking background | |
if ($node['_invoking'] && $node['_receiving']) { | |
$exportedNode['fill'] = 'url(#' . $this->prefix . '_gradient_rcv_inv)'; | |
} else if ($node['_invoking']) { | |
$exportedNode['fill'] = '#ffff88'; | |
} else if ($node['_receiving']) { | |
$exportedNode['fill'] = '#aaddff'; | |
} else if ($node['_potential_receiving']) { | |
$exportedNode['fill'] = '#eeeeff'; | |
$exportedNode['fill'] = 'url(#' . $this->prefix . '_gradient_pos_rcv)'; | |
} | |
return $exportedNode; | |
} | |
/** | |
* Returns modified $exportedEdge which become the edge's attributes. | |
*/ | |
public function processEdgeAttrs(Edge $edge, array $exportedEdge): array | |
{ | |
if (isset($edge['name'])) { | |
$label = trim($edge['name']); | |
if ($edge['_generated'] && $label != '') { | |
$label = "($label)"; | |
} | |
$exportedEdge['label'] = $label; | |
} | |
$exportedEdge['tooltip'] = json_encode($edge->getAttributes(), | |
JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); | |
// Low opacity for generated or removed edges | |
if ($edge['_generated'] || !empty($edge['_unused'])) { | |
$exportedEdge['opacity'] = '0.4'; | |
} | |
// Edge color | |
if ($edge['_transition']) { | |
$exportedEdge['color'] = "#2266cc"; | |
} else { | |
if ($edge['_state']) { | |
$exportedEdge['color'] = "#66aa22"; | |
} else { | |
$exportedEdge['color'] = "#666666"; | |
} | |
} | |
// Edge style | |
switch ($edge['type']) { | |
case 'sequenceFlow': | |
if ($edge['_dependency_only']) { | |
$exportedEdge['hidden'] = true; | |
} | |
break; | |
case 'messageFlow': | |
$exportedEdge['stroke_dasharray'] = '5,4'; | |
$exportedEdge['arrowHead'] = 'empty'; | |
$exportedEdge['arrowTail'] = 'odot'; | |
break; | |
case 'association': | |
$exportedEdge['color'] = '#aaaaaa'; | |
$exportedEdge['stroke_dasharray'] = '5,4'; | |
$exportedEdge['arrowHead'] = 'none'; | |
break; | |
case 'error': | |
$exportedEdge['color'] = '#ff0000'; | |
$exportedEdge['stroke_dasharray'] = '5,4'; | |
$exportedEdge['arrowHead'] = 'none'; | |
break; | |
default: | |
$exportedEdge['color'] = '#ff0000'; | |
break; | |
} | |
return $exportedEdge; | |
} | |
public function getExtraSvgElements(Graph $graph): array | |
{ | |
return [ | |
['defs', [], [ | |
['linearGradient', ['id' => $this->prefix . '_gradient_rcv_inv'], [ | |
['stop', ['offset' => '50%', 'stop-color' => '#ff8']], | |
['stop', ['offset' => '50%', 'stop-color' => '#adf']], | |
]], | |
['linearGradient', ['id' => $this->prefix . '_gradient_pos_rcv'], [ | |
['stop', ['offset' => '50%', 'stop-color' => '#fff']], | |
['stop', ['offset' => '50%', 'stop-color' => '#adf']], | |
]], | |
]], | |
]; | |
} | |
} |