Completed
Push — master ( c50688...111076 )
by Oliver
02:27
created

StateCollectionMerger::addTransition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

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