Completed
Push — master ( 0c0824...37a545 )
by Edward
04:26
created

Fetcher::fetchDeepProperties()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 8
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 14
ccs 0
cts 9
cp 0
crap 12
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace Remorhaz\JSON\Path\Runtime;
5
6
use function array_push;
7
use Iterator;
8
use function max;
9
use Remorhaz\JSON\Data\Value\ArrayValueInterface;
10
use Remorhaz\JSON\Path\Value\EvaluatedValueInterface;
11
use Remorhaz\JSON\Path\Value\EvaluatedValueListInterface;
12
use Remorhaz\JSON\Data\Value\NodeValueInterface;
13
use Remorhaz\JSON\Path\Value\NodeValueListBuilder;
14
use Remorhaz\JSON\Path\Value\NodeValueListInterface;
15
use Remorhaz\JSON\Data\Value\ObjectValueInterface;
16
use Remorhaz\JSON\Data\Value\ScalarValueInterface;
17
use Remorhaz\JSON\Data\Iterator\ValueIteratorFactory;
18
use Remorhaz\JSON\Path\Value\ValueListInterface;
19
20
final class Fetcher
21
{
22
23
    private $valueIteratorFactory;
24
25
    public function __construct(ValueIteratorFactory $valueIteratorFactory)
26
    {
27
        $this->valueIteratorFactory = $valueIteratorFactory;
28
    }
29
30
    /**
31
     * @param NodeValueListInterface $source
32
     * @param Matcher\ChildMatcherInterface ...$matcherList
33
     * @return NodeValueListInterface
34
     */
35
    public function fetchChildren(
36
        NodeValueListInterface $source,
37
        Matcher\ChildMatcherInterface ...$matcherList
38
    ): NodeValueListInterface {
39
        $nodesBuilder = new NodeValueListBuilder;
40
        foreach ($source->getValues() as $sourceIndex => $sourceValue) {
41
            $matcher = $matcherList[$sourceIndex];
42
            $children = $this->fetchValueChildren($matcher, $sourceValue);
43
            $outerIndex = $source->getIndexMap()->getOuterIndex($sourceIndex);
44
            foreach ($children as $child) {
45
                $nodesBuilder->addValue($child, $outerIndex);
46
            }
47
        }
48
49
        return $nodesBuilder->build();
50
    }
51
52
    public function fetchDeepChildren(
53
        Matcher\ChildMatcherInterface $matcher,
54
        NodeValueListInterface $source
55
    ): NodeValueListInterface {
56
        $nodesBuilder = new NodeValueListBuilder;
57
        foreach ($source->getValues() as $sourceIndex => $sourceValue) {
58
            $children = $this->fetchValueDeepChildren($matcher, $sourceValue);
59
            $outerIndex = $source->getIndexMap()->getOuterIndex($sourceIndex);
60
            foreach ($children as $child) {
61
                $nodesBuilder->addValue($child, $outerIndex);
62
            }
63
        }
64
65
        return $nodesBuilder->build();
66
    }
67
68
    /**
69
     * @param Matcher\ChildMatcherInterface $matcher
70
     * @param NodeValueInterface $value
71
     * @return NodeValueInterface[]
72
     */
73
    private function fetchValueChildren(
74
        Matcher\ChildMatcherInterface $matcher,
75
        NodeValueInterface $value
76
    ): array {
77
        if ($value instanceof ScalarValueInterface) {
78
            return [];
79
        }
80
81
        if ($value instanceof ArrayValueInterface) {
82
            return $this->fetchElements($value->createIterator(), $matcher);
83
        }
84
85
        if ($value instanceof ObjectValueInterface) {
86
            return $this->fetchProperties($value->createIterator(), $matcher);
87
        }
88
89
        throw new Exception\UnexpectedNodeValueFetchedException($value);
90
    }
91
92
    private function fetchValueDeepChildren(
93
        Matcher\ChildMatcherInterface $matcher,
94
        NodeValueInterface $value
95
    ): array {
96
        if ($value instanceof ScalarValueInterface) {
97
            return [];
98
        }
99
100
        if ($value instanceof ArrayValueInterface) {
101
            return $this->fetchDeepElements($value->createIterator(), $matcher);
102
        }
103
104
        if ($value instanceof ObjectValueInterface) {
105
            return $this->fetchDeepProperties($value->createIterator(), $matcher);
106
        }
107
108
        throw new Exception\UnexpectedNodeValueFetchedException($value);
109
    }
110
111
    private function fetchDeepElements(Iterator $iterator, Matcher\ChildMatcherInterface $matcher): array
112
    {
113
        $results = [];
114
        foreach ($this->valueIteratorFactory->createArrayIterator($iterator) as $index => $element) {
115
            if ($matcher->match($index)) {
116
                $results[] = $element;
117
            }
118
            array_push(
119
                $results,
120
                ...$this->fetchValueDeepChildren($matcher, $element)
121
            );
122
        }
123
124
        return $results;
125
    }
126
127
    private function fetchDeepProperties(Iterator $iterator, Matcher\ChildMatcherInterface $matcher): array
128
    {
129
        $results = [];
130
        foreach ($this->valueIteratorFactory->createObjectIterator($iterator) as $name => $property) {
131
            if ($matcher->match($name)) {
132
                $results[] = $property;
133
            }
134
            array_push(
135
                $results,
136
                ...$this->fetchValueDeepChildren($matcher, $property)
137
            );
138
        }
139
140
        return $results;
141
    }
142
143
    public function fetchFilterContext(NodeValueListInterface $source): NodeValueListInterface
144
    {
145
        $nodesBuilder = new NodeValueListBuilder;
146
        foreach ($source->getValues() as $sourceIndex => $sourceValue) {
147
            if (!$sourceValue instanceof NodeValueInterface) {
148
                throw new Exception\InvalidContextValueException($sourceValue);
149
            }
150
            $outerIndex = $source->getIndexMap()->getOuterIndex($sourceIndex);
151
            $children = $sourceValue instanceof ArrayValueInterface
152
                ? $this->fetchValueChildren(new Matcher\AnyChildMatcher, $sourceValue)
153
                : [$sourceValue];
154
            foreach ($children as $child) {
155
                $nodesBuilder->addValue($child, $outerIndex);
156
            }
157
        }
158
159
        return $nodesBuilder->build();
160
    }
161
162
    public function fetchFilteredValues(
163
        EvaluatedValueListInterface $results,
164
        NodeValueListInterface $values
165
    ): NodeValueListInterface {
166
        if (!$values->getIndexMap()->equals($results->getIndexMap())) {
167
            throw new Exception\IndexMapMatchFailedException($values, $results);
168
        }
169
        $nodesBuilder = new NodeValueListBuilder;
170
        foreach ($values->getValues() as $index => $value) {
171
            $evaluatedValue = $results->getValue($index);
172
            if (!$evaluatedValue instanceof EvaluatedValueInterface) {
173
                throw new Exception\InvalidFilterValueException($evaluatedValue);
174
            }
175
            if (!$evaluatedValue->getData()) {
176
                continue;
177
            }
178
            $nodesBuilder->addValue(
179
                $value,
180
                $values->getIndexMap()->getOuterIndex($index)
181
            );
182
        }
183
184
        return $nodesBuilder->build();
185
    }
186
187
    private function fetchElements(Iterator $iterator, Matcher\ChildMatcherInterface $matcher): array
188
    {
189
        $results = [];
190
        foreach ($this->valueIteratorFactory->createArrayIterator($iterator) as $index => $element) {
191
            if ($matcher->match($index)) {
192
                $results[] = $element;
193
            }
194
        }
195
196
        return $results;
197
    }
198
199
    private function fetchProperties(Iterator $iterator, Matcher\ChildMatcherInterface $matcher): array
200
    {
201
        $results = [];
202
        foreach ($this->valueIteratorFactory->createObjectIterator($iterator) as $name => $property) {
203
            if ($matcher->match($name)) {
204
                $results[] = $property;
205
            }
206
        }
207
208
        return $results;
209
    }
210
211
    /**
212
     * @param ValueListInterface $valueList
213
     * @return int[][]
214
     */
215
    public function fetchIndice(ValueListInterface $valueList): array
216
    {
217
        $result = [];
218
        foreach ($valueList->getValues() as $valueIndex => $value) {
219
            if (!$value instanceof ArrayValueInterface) {
220
                $result[$valueIndex] = [];
221
                continue;
222
            }
223
224
            $indice = [];
225
            $elementIterator = $this->valueIteratorFactory->createArrayIterator($value->createIterator());
226
            foreach ($elementIterator as $index => $element) {
227
                $indice[] = $index;
228
            }
229
            $result[$valueIndex] = $indice;
230
        }
231
232
        return $result;
233
    }
234
235
    /**
236
     * @param ValueListInterface $valueList
237
     * @param int|null $start
238
     * @param int|null $end
239
     * @param int|null $step
240
     * @return int[][]
241
     */
242
    public function fetchSliceIndice(ValueListInterface $valueList, ?int $start, ?int $end, ?int $step): array
243
    {
244
        $fullIndexList = $this->fetchIndice($valueList);
245
246
        $result = [];
247
        foreach ($fullIndexList as $valueIndex => $allIndice) {
248
            if (!isset($step)) {
249
                $step = 1;
250
            }
251
            $isReverse = $step < 0;
252
            $indexCount = count($allIndice);
253
            if (!isset($start)) {
254
                $start = $isReverse ? -1 : 0;
255
            }
256
            if ($start < 0) {
257
                $start = max($start + $indexCount, 0);
258
            }
259
            if (!isset($end)) {
260
                $end = $isReverse ? -$indexCount - 1 : $indexCount;
261
            }
262
            if ($end > $indexCount) {
263
                $end = $indexCount;
264
            }
265
            if ($end < 0) {
266
                $end = max($end + $indexCount, $isReverse ? -1 : 0);
267
            }
268
            $indice = [];
269
            $index = $start;
270
            while ($isReverse ? $index > $end : $index < $end) {
271
                $indice[] = $allIndice[$index];
272
                $index += $step;
273
            }
274
            $result[] = $indice;
275
        }
276
277
        return $result;
278
    }
279
}
280