Passed
Push — master ( 84fcc2...240e96 )
by Peter
04:51 queued 10s
created

YamlIndentDataFactory::isClassicHierarchy()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 2
nc 2
nop 3
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace YamlStandards\Model\YamlIndent;
6
7
use YamlStandards\Model\Component\YamlService;
8
9
class YamlIndentDataFactory
10
{
11
    /**
12
     * @param string[] $fileLines
13
     * @param int $key
14
     * @param int $countOfIndents
15
     * @param string $fileLine current checked line in loop
16
     * @param bool $isCommentLine
17
     * @return string
18
     */
19 9
    public function getRightFileLines(array $fileLines, int $key, int $countOfIndents, string $fileLine, bool $isCommentLine = false): string
20
    {
21 9
        if (YamlService::isLineComment($fileLines[$key])) {
22 6
            $key++;
23 6
            return $this->getRightFileLines($fileLines, $key, $countOfIndents, $fileLine, true);
24
        }
25
26 9
        $line = $fileLines[$key];
27 9
        $trimmedLine = trim($line);
28 9
        $countOfRowIndents = YamlService::rowIndentsOf($line);
29 9
        $explodedLine = explode(':', $line);
30
31
        // empty line
32 9
        if (YamlService::isLineBlank($line)) {
33 9
            $fileRows = array_keys($fileLines);
34 9
            $lastFileRow = end($fileRows);
35
            /* set comment line indents by next non-empty line, e.g
36
                (empty line)
37
                # comment line
38
                (empty line)
39
                foo: bar
40
            */
41 9
            if ($isCommentLine && $lastFileRow !== $key) {
42 3
                $key++;
43 3
                return $this->getRightFileLines($fileLines, $key, $countOfIndents, $fileLine, true);
44
            }
45
46 9
            $correctIndents = YamlService::createCorrectIndentsByCountOfIndents(0);
47 9
            $trimmedFileLine = trim($fileLine);
48
49 9
            return $correctIndents . $trimmedFileLine;
50
        }
51
52
        // the highest parent
53 9
        if ($countOfRowIndents === 0) {
54
            // line is directive
55 9
            if (YamlService::hasLineThreeDashesOnStartOfLine($trimmedLine)) {
56 6
                $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfRowIndents);
57 6
                $trimmedFileLine = trim($fileLine);
58
59 6
                return $correctIndents . $trimmedFileLine;
60
            }
61
62
            // parent start as array, e.g. "- foo: bar"
63
            // skip comment line because we want result after this condition
64 9
            if ($isCommentLine === false && YamlService::isLineStartOfArrayWithKeyAndValue($trimmedLine)) {
65 3
                return $this->getCorrectLineForArrayWithKeyAndValue($line, $fileLines, $key, $countOfIndents, $fileLine, $isCommentLine);
66
            }
67
68 9
            $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfRowIndents);
69 9
            $trimmedFileLine = trim($fileLine);
70
71 9
            return $correctIndents . $trimmedFileLine;
72
        }
73
74
        // line start of array, e.g. "- foo: bar" or "- foo" or "- { foo: bar }" or "- foo:"
75 9
        if (YamlService::isLineStartOfArrayWithKeyAndValue($trimmedLine)) {
76 9
            return $this->getCorrectLineForArrayWithKeyAndValue($line, $fileLines, $key, $countOfIndents, $fileLine, $isCommentLine);
77
        }
78
79
        // children of array, description over name of function
80 9
        if ($this->belongLineToArray($fileLines, $key)) {
81 6
            $countOfParents = $this->getCountOfParentsForLine($fileLines, $key);
82 6
            $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfParents * $countOfIndents);
83 6
            $trimmedFileLine = trim($fileLine);
84
85 6
            return $correctIndents . $trimmedFileLine;
86
        }
87
88
        // line without ':', e.g. array or string
89 9
        if (array_key_exists(1, $explodedLine) === false) {
90
            // is multidimensional array?
91 9
            if ($trimmedLine === '-') {
92 6
                $countOfParents = $this->getCountOfParentsForLine($fileLines, $key);
93
94 6
                $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfParents * $countOfIndents);
95 6
                $trimmedFileLine = trim($fileLine);
96
97 6
                return $correctIndents . $trimmedFileLine;
98
            }
99
100
            // is array or string?
101 6
            $countOfParents = $this->getCountOfParentsForLine($fileLines, $key);
102 6
            $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfParents * $countOfIndents);
103 6
            $trimmedFileLine = trim($fileLine);
104
105 6
            return $correctIndents . $trimmedFileLine;
106
        }
107
108 9
        $lineValue = $explodedLine[1];
109 9
        $trimmedLineValue = trim($lineValue);
110
111
        // parent, not comment line
112 9
        if ($isCommentLine === false && (YamlService::isLineBlank($lineValue) || YamlService::isValueReuseVariable($trimmedLineValue))) {
113
            // fix situation when key is without value and is not parent, e.g.: "   foo:"
114 9
            $nextLine = array_key_exists($key + 1, $fileLines) ? $fileLines[$key + 1] : '';
115 9
            if (YamlService::rowIndentsOf($nextLine) > $countOfRowIndents) {
116 9
                $countOfParents = $this->getCountOfParentsForLine($fileLines, $key);
117
118 9
                $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfParents * $countOfIndents);
119 9
                $trimmedFileLine = trim($fileLine);
120
121 9
                return $correctIndents . $trimmedFileLine;
122
            }
123
        }
124
125 9
        $countOfParents = $this->getCountOfParentsForLine($fileLines, $key);
126 9
        $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfParents * $countOfIndents);
127 9
        $trimmedFileLine = trim($fileLine);
128
129 9
        return $correctIndents . $trimmedFileLine;
130
    }
131
132
    /**
133
     * Belong line to children of array, e.g.
134
     * - foo: bar
135
     *   baz: qux
136
     *   quux: quuz
137
     *   etc.: etc.
138
     *
139
     * @param string[] $fileLines
140
     * @param int $key
141
     * @return bool
142
     */
143 9
    private function belongLineToArray(array $fileLines, int $key): bool
144
    {
145 9
        while ($key >= 0) {
146 9
            $line = $fileLines[$key];
147 9
            $key--;
148 9
            $prevLine = $fileLines[$key];
149 9
            $trimmedPrevLine = trim($prevLine);
150
151 9
            if (YamlService::hasLineDashOnStartOfLine($trimmedPrevLine)) {
152 9
                $prevLine = preg_replace('/-/', ' ', $prevLine, 1); // replace '-' for space
153
            }
154
155 9
            if (YamlService::rowIndentsOf($prevLine) === YamlService::rowIndentsOf($line)) {
156 9
                if (YamlService::isLineStartOfArrayWithKeyAndValue($trimmedPrevLine)) {
157 9
                    return true;
158
                }
159
            } else {
160 9
                break;
161
            }
162
        }
163
164 9
        return false;
165
    }
166
167
    /**
168
     * line start of array, e.g. "- foo: bar" or "- foo" or "- { foo: bar }"
169
     *
170
     * @param string $line
171
     * @param string[] $fileLines
172
     * @param int $key
173
     * @param int $countOfIndents
174
     * @param string $fileLine current checked line in loop
175
     * @param bool $isCommentLine
176
     * @return string
177
     */
178 9
    private function getCorrectLineForArrayWithKeyAndValue(string $line, array $fileLines, int $key, int $countOfIndents, string $fileLine, bool $isCommentLine): string
179
    {
180 9
        $lineWithReplacedDashToSpace = preg_replace('/-/', ' ', $line, 1);
181 9
        $trimmedLineWithoutDash = trim($lineWithReplacedDashToSpace);
182
183 9
        $countOfParents = $this->getCountOfParentsForLine($fileLines, $key);
184 9
        $correctIndentsOnStartOfLine = YamlService::createCorrectIndentsByCountOfIndents($countOfParents * $countOfIndents);
185
186 9
        $trimmedFileLine = trim($fileLine);
187 9
        if ($isCommentLine) {
188 6
            return $correctIndentsOnStartOfLine . $trimmedFileLine;
189
        }
190
191
        // solution "- { foo: bar }"
192 9
        if (YamlService::isCurlyBracketInStartOfString($trimmedLineWithoutDash)) {
193 6
            $correctIndentsBetweenDashAndBracket = YamlService::createCorrectIndentsByCountOfIndents(1);
194
195 6
            return $correctIndentsOnStartOfLine . '-' . $correctIndentsBetweenDashAndBracket . $trimmedLineWithoutDash;
196
        }
197
198
        // solution "- foo" (single value of an array)
199 7
        if (YamlService::isKeyInStartOfString($trimmedLineWithoutDash) === false) {
200 4
            $correctIndentsBetweenDashAndKey = YamlService::createCorrectIndentsByCountOfIndents(1);
201
202 4
            return $correctIndentsOnStartOfLine . '-' . $correctIndentsBetweenDashAndKey . $trimmedLineWithoutDash;
203
        }
204
205
        /**
206
         * solution for one or more values in array
207
         * "- foo: bar"
208
         * "  baz: qux"
209
         */
210 6
        $correctIndentsBetweenDashAndKey = YamlService::createCorrectIndentsByCountOfIndents($countOfIndents - 1); // 1 space is dash, dash is as indent
211
212 6
        return $correctIndentsOnStartOfLine . '-' . $correctIndentsBetweenDashAndKey . $trimmedLineWithoutDash;
213
    }
214
215
    /**
216
     * Go back until deepest parent and count them
217
     *
218
     * @param string[] $fileLines
219
     * @param int $key
220
     * @return int
221
     */
222 10
    private function getCountOfParentsForLine(array $fileLines, int $key): int
223
    {
224 10
        $countOfParents = 0;
225 10
        $line = $fileLines[$key];
226 10
        $originalLine = $line;
227 10
        $countOfRowIndents = YamlService::rowIndentsOf($line);
228 10
        $trimmedLine = trim($line);
229 10
        $isArrayLine = YamlService::hasLineDashOnStartOfLine($trimmedLine);
230
231 10
        while ($key > 0) {
232 10
            $currentLine = $fileLines[$key];
233 10
            $countOfCurrentRowIndents = YamlService::rowIndentsOf($currentLine);
234 10
            $key--;
235 10
            $prevLine = $fileLines[$key];
236 10
            $trimmedPrevLine = trim($prevLine);
237 10
            $isPrevLineArrayLine = YamlService::hasLineDashOnStartOfLine($trimmedPrevLine);
238 10
            $countOfPrevRowIndents = YamlService::rowIndentsOf($prevLine);
239
240
            // ignore comment line and empty line
241 10
            if (YamlService::isLineBlank($prevLine) || YamlService::isLineComment($prevLine)) {
242 6
                continue;
243
            }
244
245 10
            if ($this->isPrevLineNextParent($isArrayLine, $countOfPrevRowIndents, $countOfRowIndents, $isPrevLineArrayLine)) {
246 10
                $line = $fileLines[$key];
247 10
                $countOfRowIndents = YamlService::rowIndentsOf($line);
248 10
                $trimmedLine = trim($line);
249 10
                $isArrayLine = YamlService::hasLineDashOnStartOfLine($trimmedLine);
250
251
                /*
252
                 * array has array values at beginning and is not first element in array, e.g.
253
                 *  -  pathsToCheck:
254
                 *      - path/to/file
255
                 */
256 10
                if ($isArrayLine &&
257 10
                    YamlService::isLineOpeningAnArray($trimmedLine) &&
258 10
                    YamlService::rowIndentsOf($line) === 0 &&
259 10
                    $countOfParents > 0
260
                ) {
261 4
                    $countOfParents--;
262
                }
263
264 10
                $countOfParents++;
265
266
                /* nested hierarchy in array fix, eg.
267
                   - foo:
268
                       nested: value
269
                     bar: baz
270
                */
271 10
                if ($isArrayLine && YamlService::isLineOpeningAnArray($trimmedLine) && YamlService::keyIndentsOf($originalLine) > YamlService::keyIndentsOf($line)) {
272 4
                    $countOfParents++;
273
                }
274
            }
275
276
            // if line has zero counts of indents then it's highest parent and should be ended
277 10
            if ($countOfRowIndents === 0) {
278
                /**
279
                 * find parent if line belong to array, if it exists then add one parent to count of parents variable, e.g.
280
                 * foo:
281
                 * - bar
282
                 */
283 10
                if (YamlService::isLineStartOfArrayWithKeyAndValue($trimmedLine)) {
284 4
                    while ($key >= 0) {
285 4
                        $prevLine = $fileLines[$key];
286 4
                        $countOfPrevRowIndents = YamlService::rowIndentsOf($prevLine);
287 4
                        if (YamlService::isLineBlank($prevLine) || YamlService::isLineComment($prevLine)) {
288 3
                            $key--;
289 3
                            continue;
290
                        }
291
292
                        /**
293
                         * 'qux' is highest parent, so skip this, e.g.
294
                         * - foo:
295
                         *      bar: baz
296
                         * - qux:
297
                         *      - quux: quuz
298
                         */
299 4
                        if ($countOfPrevRowIndents > $countOfCurrentRowIndents) {
300 4
                            break;
301
                        }
302
303 4
                        if ($countOfPrevRowIndents === 0 &&
304 4
                            $countOfPrevRowIndents === $countOfCurrentRowIndents &&
305 4
                            YamlService::hasLineColon($prevLine) &&
306 4
                            YamlService::hasLineValue($prevLine) === false
307
                        ) {
308 3
                            $countOfParents++;
309
310 3
                            break;
311
                        }
312
313 4
                        $currentLine = $fileLines[$key];
314 4
                        $countOfCurrentRowIndents = YamlService::rowIndentsOf($currentLine);
315 4
                        $key--;
316
                    }
317
                }
318
319 10
                break;
320
            }
321
        }
322
323 10
        return $countOfParents;
324
    }
325
326
    /**
327
     * @param bool $isArrayLine
328
     * @param int $countOfPrevRowIndents
329
     * @param int $countOfRowIndents
330
     * @param bool $isPrevLineArrayLine
331
     * @return bool
332
     */
333 11
    private function isPrevLineNextParent(bool $isArrayLine, int $countOfPrevRowIndents, int $countOfRowIndents, bool $isPrevLineArrayLine): bool
334
    {
335 11
        return $this->isClassicHierarchy($isArrayLine, $countOfPrevRowIndents, $countOfRowIndents) ||
336 11
            $this->isStartOfArray($isArrayLine, $countOfPrevRowIndents, $countOfRowIndents, $isPrevLineArrayLine) ||
337 11
            $this->isStartOfArrayInArray($isArrayLine, $countOfPrevRowIndents, $countOfRowIndents, $isPrevLineArrayLine);
338
    }
339
340
    /**
341
     * @param bool $isArrayLine
342
     * @param int $countOfPrevRowIndents
343
     * @param int $countOfRowIndents
344
     * @return bool
345
     *
346
     * @example
347
     * foo:
348
     *     bar: baz
349
     */
350 12
    private function isClassicHierarchy(bool $isArrayLine, int $countOfPrevRowIndents, int $countOfRowIndents): bool
351
    {
352 12
        return $isArrayLine === false && $countOfPrevRowIndents < $countOfRowIndents;
353
    }
354
355
    /**
356
     * @param bool $isArrayLine
357
     * @param int $countOfPrevRowIndents
358
     * @param int $countOfRowIndents
359
     * @param bool $isPrevLineArrayLine
360
     * @return bool
361
     *
362
     * @example
363
     * foo:
364
     *     - bar: baz
365
     */
366 12
    private function isStartOfArray(bool $isArrayLine, int $countOfPrevRowIndents, int $countOfRowIndents, bool $isPrevLineArrayLine): bool
367
    {
368 12
        return $isArrayLine && $countOfPrevRowIndents <= $countOfRowIndents && $isPrevLineArrayLine === false;
369
    }
370
371
    /**
372
     * @param bool $isArrayLine
373
     * @param int $countOfPrevRowIndents
374
     * @param int $countOfRowIndents
375
     * @param bool $isPrevLineArrayLine
376
     * @return bool
377
     *
378
     * @example
379
     * foo:
380
     *     - bar:
381
     *         - 'any text'
382
     */
383 12
    private function isStartOfArrayInArray(bool $isArrayLine, int $countOfPrevRowIndents, int $countOfRowIndents, bool $isPrevLineArrayLine): bool
384
    {
385 12
        return $isArrayLine && $countOfPrevRowIndents < $countOfRowIndents && $isPrevLineArrayLine;
386
    }
387
}
388