Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
57.14% covered (warning)
57.14%
4 / 7
CRAP
76.03% covered (warning)
76.03%
111 / 146
GrafovatkoProcessor
0.00% covered (danger)
0.00%
0 / 1
57.14% covered (warning)
57.14%
4 / 7
114.96
76.03% covered (warning)
76.03%
111 / 146
 __construct
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 setPrefix
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 setTargetParticipant
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 processGraph
0.00% covered (danger)
0.00%
0 / 1
6.35
78.57% covered (warning)
78.57%
11 / 14
 processNodeAttrs
0.00% covered (danger)
0.00%
0 / 1
59.92
74.42% covered (warning)
74.42%
64 / 86
 processEdgeAttrs
0.00% covered (danger)
0.00%
0 / 1
16.34
72.97% covered (warning)
72.97%
27 / 37
 getExtraSvgElements
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
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']],
                ]],
            ]],
        ];
    }
}