Completed
Branch master (755019)
by Oliver
12:48 queued 05:47
created

StateCollectionMerger   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 21.43%

Importance

Changes 12
Bugs 6 Features 4
Metric Value
wmc 30
c 12
b 6
f 4
lcom 1
cbo 7
dl 0
loc 209
ccs 15
cts 70
cp 0.2143
rs 10

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getStateNamePrefix() 0 4 1
A setStateNamePrefix() 0 4 1
A getTargetCollection() 0 4 1
A createState() 0 4 1
A addTransition() 0 8 2
A createTransition() 0 19 3
B mergeMetadata() 0 17 5
A addState() 0 8 2
A findOrCreateState() 0 12 2
B mergeState() 0 23 4
A mergeStateCollection() 0 7 2
B merge() 0 14 5
1
<?php
2
3
namespace Metabor\Statemachine\Util;
4
5
use Metabor\Statemachine\State;
6
use Metabor\Statemachine\StateCollection;
7
use Metabor\Statemachine\Transition;
8
use MetaborStd\MergeableInterface;
9
use MetaborStd\MetadataInterface;
10
use MetaborStd\Statemachine\StateCollectionInterface;
11
use MetaborStd\Statemachine\StateInterface;
12
use MetaborStd\Statemachine\TransitionInterface;
13
14
/**
15
 * @author Oliver Tischlinger
16
 */
17
class StateCollectionMerger implements MergeableInterface
18
{
19
    /**
20
     * @var StateCollectionInterface
21
     */
22
    private $targetCollection;
23
24
    /**
25
     * @var string
26
     */
27
    private $stateNamePrefix;
28
29
    /**
30
     * @param StateCollectionInterface $targetCollection
31
     */
32 4
    public function __construct(StateCollectionInterface $targetCollection)
33
    {
34 4
        $this->targetCollection = $targetCollection;
35
    }
36
37
    /**
38
     * @return string
39
     */
40
    public function getStateNamePrefix()
41
    {
42
        return $this->stateNamePrefix;
43
    }
44
45
    /**
46
     * @param string $stateNamePrefix
47
     */
48
    public function setStateNamePrefix($stateNamePrefix)
49
    {
50
        $this->stateNamePrefix = $stateNamePrefix;
51
    }
52
53
    /**
54
     * @return \MetaborStd\Statemachine\StateCollectionInterface
55
     */
56
    public function getTargetCollection()
57
    {
58
        return $this->targetCollection;
59
    }
60
61
    /**
62
     * @param string $name
63
     *
64
     * @return \MetaborStd\Statemachine\StateInterface
65
     */
66
    protected function createState($name)
67
    {
68
        return new State($name);
69
    }
70
71
    /**
72
     * @param StateInterface      $sourceState
73
     * @param TransitionInterface $sourceTransition
74
     *
75
     * @throws \InvalidArgumentException
76
     */
77
    protected function addTransition(StateInterface $sourceState, TransitionInterface $sourceTransition)
78
    {
79
        if ($sourceState instanceof State) {
80
            $sourceState->addTransition($sourceTransition);
81
        } else {
82
            throw new \InvalidArgumentException('Overwrite this method to implement a different type!');
83
        }
84
    }
85
86
    /**
87
     * @param TransitionInterface $sourceTransition
88
     *
89
     * @throws \InvalidArgumentException
90
     *
91
     * @return \Metabor\Statemachine\Transition
92
     */
93 3
    protected function createTransition(TransitionInterface $sourceTransition)
94
    {
95
        $targetStateName = $sourceTransition->getTargetState()->getName();
96
        $targetState = $this->findOrCreateState($targetStateName);
97
        $this->mergeMetadata($sourceTransition->getTargetState(), $targetState);
98
        $eventName = $sourceTransition->getEventName();
99
100
        if ($sourceTransition->getConditionName()) {
101 3
            if ($sourceTransition instanceof Transition) {
102
                $condition = $sourceTransition->getCondition();
103
            } else {
104
                throw new \InvalidArgumentException('Overwrite this method to implement a different type!');
105 3
            }
106
        } else {
107 3
            $condition = null;
108 3
        }
109
110
        return new Transition($targetState, $eventName, $condition);
111
    }
112
113
    /**
114
     * @param object $source
115
     * @param object $target
116
     *
117
     * @throws \RuntimeException
118
     */
119
    protected function mergeMetadata($source, $target)
120
    {
121
        if ($source instanceof \ArrayAccess) {
122
            if ($target instanceof \ArrayAccess) {
123
                if ($source instanceof MetadataInterface) {
124
                    $metadata = $source->getMetadata();
125
                    foreach ($metadata as $offset => $value) {
126
                        $target->offsetSet($offset, $value);
127
                    }
128
                } else {
129
                    throw new \RuntimeException('Source had to make all metadata available!');
130
                }
131
            } else {
132
                throw new \RuntimeException('Source metadata can not be merged!');
133
            }
134
        }
135
    }
136
137
    /**
138
     * @param StateInterface $state
139
     *
140
     * @throws \InvalidArgumentException
141
     */
142
    protected function addState(StateInterface $state)
143
    {
144
        if ($this->targetCollection instanceof StateCollection) {
145
            $this->targetCollection->addState($state);
146
        } else {
147
            throw new \InvalidArgumentException('TargetCollection has to be a StateCollection. Overwrite this method to implement a different type!');
148
        }
149
    }
150
151
    /**
152
     * @param string $name
153
     *
154
     * @return \MetaborStd\Statemachine\StateInterface
155
     */
156
    protected function findOrCreateState($name)
157
    {
158
        $name = $this->stateNamePrefix . $name;
159
        if ($this->targetCollection->hasState($name)) {
160
            $targetState = $this->targetCollection->getState($name);
161
        } else {
162
            $targetState = $this->createState($name);
163
            $this->addState($targetState);
164
        }
165
166
        return $targetState;
167
    }
168
169
    /**
170
     * @param StateInterface $sourceState
171
     *
172
     * @throws \InvalidArgumentException
173
     */
174 3
    protected function mergeState(StateInterface $sourceState)
175
    {
176
        $name = $sourceState->getName();
177
        $targetState = $this->findOrCreateState($name);
178
        $this->mergeMetadata($sourceState, $targetState);
179
180
        /* @var $transition TransitionInterface */
181
        foreach ($sourceState->getTransitions() as $sourceTransition) {
182
            $targetTransition = $this->createTransition($sourceTransition);
183
            $this->addTransition($targetState, $targetTransition);
184
        }
185
186
        foreach ($sourceState->getEventNames() as $eventName) {
187
            $sourceEvent = $sourceState->getEvent($eventName);
188
            $targetEvent = $targetState->getEvent($eventName);
189
190
            $this->mergeMetadata($sourceEvent, $targetEvent);
191
192 3
            foreach ($sourceEvent->getObservers() as $observer) {
193
                $targetEvent->attach($observer);
194
            }
195
        }
196
    }
197
198
    /**
199
     */
200 4
    protected function mergeStateCollection(StateCollectionInterface $source)
201
    {
202
        /* @var $sourceState StateInterface */
203 4
        foreach ($source->getStates() as $sourceState) {
204
            $this->mergeState($sourceState);
205
        }
206
    }
207
208
    /**
209
     * @see \MetaborStd\MergeableInterface::merge()
210
     */
211 4
    public function merge($source)
212
    {
213 4
        if ($source instanceof StateCollectionInterface) {
214
            $this->mergeStateCollection($source);
215
        } elseif ($source instanceof StateInterface) {
216
            $this->mergeState($source);
217
        } elseif ($source instanceof \Traversable) {
218
            foreach ($source as $value) {
219
                $this->merge($value);
220
            }
221
        } else {
222
            throw new \InvalidArgumentException('Source can not be merged!');
223 4
        }
224 4
    }
225
}
226