GraphBuilder::getLayoutOptions()   A
last analyzed

Complexity

Conditions 5
Paths 8

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 8.7918

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 7
cts 15
cp 0.4667
rs 9.2888
c 0
b 0
f 0
cc 5
nc 8
nop 2
crap 8.7918
1
<?php
2
3
namespace Metabor\Statemachine\Graph;
4
5
use Fhaculty\Graph\Graph;
6
use Metabor\Callback\Callback;
7
use Metabor\StringConverter;
8
use MetaborStd\Event\EventInterface;
9
use MetaborStd\Statemachine\StateCollectionInterface;
10
use MetaborStd\Statemachine\StateInterface;
11
use MetaborStd\Statemachine\TransitionInterface;
12
use MetaborStd\StringConverterInterface;
13
use MetaborStd\WeightedInterface;
14
15
/**
16
 * @author otischlinger
17
 */
18
class GraphBuilder
19
{
20
    /**
21
     * @var array
22
     */
23
    private $eventLayout = array();
24
25
    /**
26
     * @var array
27
     */
28
    private $stateLayout = array();
29
30
    /**
31
     * @var \SplObjectStorage
32
     */
33
    private $layoutCallback;
34
35
    /**
36
     * @var Graph
37
     */
38
    private $graph;
39
40
    /**
41
     * @var StringConverterInterface
42
     */
43
    private $stringConverter;
44
45
    /**
46
     * @param Graph                    $graph
47
     * @param StringConverterInterface $stringConverter
48
     */
49 1
    public function __construct(Graph $graph, StringConverterInterface $stringConverter = null)
50
    {
51 1
        $this->layoutCallback = new \SplObjectStorage();
52 1
        $this->graph = $graph;
53 1
        if ($stringConverter) {
54
            $this->stringConverter = $stringConverter;
55
        } else {
56 1
            $this->stringConverter = new StringConverter();
57
        }
58 1
    }
59
60
    /**
61
     * @link http://www.graphviz.org/doc/info/attrs.html
62
     *
63
     * @param string $flag
64
     * @param scalar $value
65
     * @param array  $layout
66
     */
67
    public function setEventLayout($flag, $value, array $layout)
68
    {
69
        $value = (string) $value;
70
        $this->eventLayout[$flag][$value] = $layout;
71
    }
72
73
    /**
74
     * @link http://www.graphviz.org/doc/info/attrs.html
75
     *
76
     * @param string $flag
77
     * @param scalar $value
78
     * @param array  $layout
79
     */
80
    public function setStateLayout($flag, $value, array $layout)
81
    {
82
        $value = (string) $value;
83
        $this->stateLayout[$flag][$value] = $layout;
84
    }
85
86
    /**
87
     * @param Callback $callback
88
     */
89
    public function attachLayoutCallback(Callback $callback)
90
    {
91
        $this->layoutCallback->attach($callback);
92
    }
93
94
    /**
95
     * @param Callback $callback
96
     */
97
    public function detachLayoutCallback(Callback $callback)
98
    {
99
        $this->layoutCallback->detach($callback);
100
    }
101
102
    /**
103
     * @param \ArrayAccess $flaggedObject
104
     * @param array        $layout
105
     *
106
     * @return array
107
     */
108 1
    protected function getLayoutOptions(\ArrayAccess $flaggedObject, array $layout)
109
    {
110 1
        $result = array();
111 1
        foreach ($layout as $flag => $options) {
112
            if ($flaggedObject->offsetExists($flag)) {
113
                $value = $flaggedObject->offsetGet($flag);
114
                $value = (string) $value;
115
                if (isset($options[$value])) {
116
                    $result += $options[$value];
117
                }
118
            }
119 1
        }
120
121
        /* @var $callback callable */
122 1
        foreach ($this->layoutCallback as $callback) {
123
            $result = $callback($flaggedObject, $result);
124 1
        }
125
126 1
        return $result;
127
    }
128
129
    /**
130
     * @param StateInterface $state
131
     *
132
     * @return \Fhaculty\Graph\Vertex
133
     */
134 1
    public function createStatusVertex(StateInterface $state)
135
    {
136 1
        $stateName = $state->getName();
137 1
        $vertex = $this->graph->createVertex($stateName, true);
138 1
        if ($state instanceof \ArrayAccess) {
139 1
            $layout = $this->getLayoutOptions($state, $this->stateLayout);
140 1
            if (method_exists($vertex, 'setLayout')) {
141 1
                $vertex->setLayout($layout);
0 ignored issues
show
Bug introduced by
The method setLayout() does not seem to exist on object<Fhaculty\Graph\Vertex>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
142 1
            } else {
143
                foreach ($layout as $name => $value) {
144
                    $vertex->setAttribute('graphviz.' . $name, $value);
145
                }
146
            }
147 1
        }
148
149 1
        return $vertex;
150
    }
151
152
    /**
153
     * @param EventInterface $event
154
     *
155
     * @return string
156
     */
157
    protected function convertObserverToString(EventInterface $event)
158
    {
159
        $observers = array();
160
        foreach ($event->getObservers() as $observer) {
161
            $observers[] = $this->stringConverter->convertToString($observer);
162
        }
163
164
        return implode(', ', $observers);
165
    }
166
167
    /**
168
     * @param TransitionInterface $transition
169
     *
170
     * @return string
171
     */
172 1
    protected function getTransitionLabel(StateInterface $state, TransitionInterface $transition)
173
    {
174 1
        $labelParts = array();
175 1
        $eventName = $transition->getEventName();
176 1
        if ($eventName) {
177
            $labelParts[] = 'E: ' . $eventName;
178
            $event = $state->getEvent($eventName);
179
            $observerName = $this->convertObserverToString($event);
180
            if ($observerName) {
181
                $labelParts[] = 'C: ' . $observerName;
182
            }
183
        }
184 1
        $conditionName = $transition->getConditionName();
185 1
        if ($conditionName) {
186
            $labelParts[] = 'IF: ' . $conditionName;
187
        }
188 1
        if ($transition instanceof WeightedInterface) {
189 1
            $labelParts[] = 'W: ' . $transition->getWeight();
190 1
        }
191
192 1
        $label = implode(PHP_EOL, $labelParts);
193
194 1
        return $label;
195
    }
196
197
    /**
198
     * @param StateInterface      $state
199
     * @param TransitionInterface $transition
200
     */
201 1
    protected function addTransition(StateInterface $state, TransitionInterface $transition)
202
    {
203 1
        $sourceStateVertex = $this->createStatusVertex($state);
204 1
        $targetStateVertex = $this->createStatusVertex($transition->getTargetState());
205 1
        $edge = $sourceStateVertex->createEdgeTo($targetStateVertex);
206 1
        $label = $this->getTransitionLabel($state, $transition);
207 1
        if ($label) {
208 1
            if (method_exists($edge, 'setLayoutAttribute')) {
209 1
                $edge->setLayoutAttribute('label', $label);
0 ignored issues
show
Bug introduced by
The method setLayoutAttribute() does not seem to exist on object<Fhaculty\Graph\Edge\Directed>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
210 1
            } else {
211
                $edge->setAttribute('graphviz.label', $label);
212
            }
213 1
        }
214
215 1
        $eventName = $transition->getEventName();
216 1
        if ($eventName) {
217
            $event = $state->getEvent($eventName);
218
            if ($event instanceof \ArrayAccess) {
219
                $layout = $this->getLayoutOptions($event, $this->eventLayout);
220
                if (method_exists($edge, 'setLayout')) {
221
                    $edge->setLayout($layout);
0 ignored issues
show
Bug introduced by
The method setLayout() does not seem to exist on object<Fhaculty\Graph\Edge\Directed>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
222
                } else {
223
                    foreach ($layout as $name => $value) {
224
                        $edge->setAttribute('graphviz.' . $name, $value);
225
                    }
226
                }
227
            }
228
        }
229 1
    }
230
231
    /**
232
     * @param StateInterface $state
233
     */
234 1
    public function addState(StateInterface $state)
235
    {
236 1
        $this->createStatusVertex($state);
237
        /* @var $transition TransitionInterface */
238 1
        foreach ($state->getTransitions() as $transition) {
239 1
            $this->addTransition($state, $transition);
240 1
        }
241 1
    }
242
243
    /**
244
     * @param \Traversable $states
245
     */
246 1
    public function addStates(\Traversable $states)
247
    {
248 1
        foreach ($states as $state) {
249 1
            $this->addState($state);
250 1
        }
251 1
    }
252
253
    /**
254
     * @param StateCollectionInterface $stateCollection
255
     */
256 1
    public function addStateCollection(StateCollectionInterface $stateCollection)
257
    {
258 1
        $this->addStates($stateCollection->getStates());
259 1
    }
260
261
    /**
262
     * @return \Fhaculty\Graph\Graph
263
     */
264
    public function getGraph()
265
    {
266
        return $this->graph;
267
    }
268
}
269