StateCollectionMerger   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 81%

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 7
dl 0
loc 225
ccs 81
cts 100
cp 0.81
rs 9.84
c 0
b 0
f 0

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