Completed
Branch master (755019)
by Oliver
04:40
created

StateCollectionMerger   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 79.49%

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 62
cts 78
cp 0.7949
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 4
    }
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 4
    protected function createState($name)
67
    {
68 4
        return new State($name);
69
    }
70
71
    /**
72
     * @param StateInterface      $sourceState
73
     * @param TransitionInterface $sourceTransition
74
     *
75
     * @throws \InvalidArgumentException
76
     */
77 4
    protected function addTransition(StateInterface $sourceState, TransitionInterface $sourceTransition)
78
    {
79 4
        if ($sourceState instanceof State) {
80 4
            $sourceState->addTransition($sourceTransition);
81
        } else {
82
            throw new \InvalidArgumentException('Overwrite this method to implement a different type!');
83
        }
84 4
    }
85
86
    /**
87
     * @param TransitionInterface $sourceTransition
88
     *
89
     * @throws \InvalidArgumentException
90
     *
91
     * @return \Metabor\Statemachine\Transition
92
     */
93 4
    protected function createTransition(TransitionInterface $sourceTransition)
94
    {
95 4
        $targetStateName = $sourceTransition->getTargetState()->getName();
96 4
        $targetState = $this->findOrCreateState($targetStateName);
97 4
        $this->mergeMetadata($sourceTransition->getTargetState(), $targetState);
98 4
        $eventName = $sourceTransition->getEventName();
99
100 4
        if ($sourceTransition->getConditionName()) {
101 3
            if ($sourceTransition instanceof Transition) {
102 3
                $condition = $sourceTransition->getCondition();
103
            } else {
104 3
                throw new \InvalidArgumentException('Overwrite this method to implement a different type!');
105
            }
106
        } else {
107 4
            $condition = null;
108
        }
109
110 4
        return new Transition($targetState, $eventName, $condition);
111
    }
112
113
    /**
114
     * @param object $source
115
     * @param object $target
116
     *
117
     * @throws \RuntimeException
118
     */
119 4
    protected function mergeMetadata($source, $target)
120
    {
121 4
        if ($source instanceof \ArrayAccess) {
122 4
            if ($target instanceof \ArrayAccess) {
123 4
                if ($source instanceof MetadataInterface) {
124 4
                    $metadata = $source->getMetadata();
125 4
                    foreach ($metadata as $offset => $value) {
126 4
                        $target->offsetSet($offset, $value);
127
                    }
128
                } else {
129 4
                    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 4
    }
136
137
    /**
138
     * @param StateInterface $state
139
     *
140
     * @throws \InvalidArgumentException
141
     */
142 4
    protected function addState(StateInterface $state)
143
    {
144 4
        if ($this->targetCollection instanceof StateCollection) {
145 4
            $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 4
    }
150
151
    /**
152
     * @param string $name
153
     *
154
     * @return \MetaborStd\Statemachine\StateInterface
155
     */
156 4
    protected function findOrCreateState($name)
157
    {
158 4
        $name = $this->stateNamePrefix . $name;
159 4
        if ($this->targetCollection->hasState($name)) {
160 4
            $targetState = $this->targetCollection->getState($name);
161
        } else {
162 4
            $targetState = $this->createState($name);
163 4
            $this->addState($targetState);
164
        }
165
166 4
        return $targetState;
167
    }
168
169
    /**
170
     * @param StateInterface $sourceState
171
     *
172
     * @throws \InvalidArgumentException
173
     */
174 4
    protected function mergeState(StateInterface $sourceState)
175
    {
176 4
        $name = $sourceState->getName();
177 4
        $targetState = $this->findOrCreateState($name);
178 4
        $this->mergeMetadata($sourceState, $targetState);
179
180
        /* @var $transition TransitionInterface */
181 4
        foreach ($sourceState->getTransitions() as $sourceTransition) {
182 4
            $targetTransition = $this->createTransition($sourceTransition);
183 4
            $this->addTransition($targetState, $targetTransition);
184
        }
185
186 4
        foreach ($sourceState->getEventNames() as $eventName) {
187 4
            $sourceEvent = $sourceState->getEvent($eventName);
188 4
            $targetEvent = $targetState->getEvent($eventName);
189
190 4
            $this->mergeMetadata($sourceEvent, $targetEvent);
191
192 4
            foreach ($sourceEvent->getObservers() as $observer) {
193 4
                $targetEvent->attach($observer);
194
            }
195
        }
196 4
    }
197
198
    /**
199
     */
200 4
    protected function mergeStateCollection(StateCollectionInterface $source)
201
    {
202
        /* @var $sourceState StateInterface */
203 4
        foreach ($source->getStates() as $sourceState) {
204 4
            $this->mergeState($sourceState);
205
        }
206 4
    }
207
208
    /**
209
     * @see \MetaborStd\MergeableInterface::merge()
210
     */
211 4
    public function merge($source)
212
    {
213 4
        if ($source instanceof StateCollectionInterface) {
214 4
            $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
        }
224 4
    }
225
}
226