Passed
Push — master ( 868ce1...343c55 )
by Chris
02:16
created

PointerGenerator::getNodePath()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 1
nop 1
crap 2
1
<?php declare(strict_types=1);
2
3
namespace DaveRandom\Jom;
4
5
use DaveRandom\Jom\Exceptions\InvalidReferenceNodeException;
6
use DaveRandom\Jom\Exceptions\InvalidSubjectNodeException;
7
8
/** @noinspection PhpInconsistentReturnPointsInspection */
9
10
final class PointerGenerator
11
{
12
    /**
13
     * @var Node
14
     */
15
    private $root;
16
17
    /**
18
     * @var Node|null
19
     */
20
    private $rootParent;
21
22
    /**
23
     * @var Document
24
     */
25
    private $ownerDocument;
26
27
    /**
28
     * @param Node[] $nodePath
29
     * @return string[]
30
     */
31 11
    private function nodePathToPointerPath(array $nodePath): array
32
    {
33 11
        $result = [];
34
35 11
        for ($i = \count($nodePath) - 1; $i >= 0; $i--) {
36 7
            $result[] = $nodePath[$i]->getKey();
37
        }
38
39 11
        return $result;
40
    }
41
42
    /**
43
     * @throws InvalidSubjectNodeException
44
     * @throws InvalidReferenceNodeException
45
     */
46 11
    private function validateAndRemoveNodePathRoots(array &$targetPath, array &$basePath = null): void
47
    {
48 11
        if (\array_pop($targetPath) !== $this->root) {
49
            throw new InvalidSubjectNodeException('Target node for pointer is not a child of the generator root node');
50
        }
51
52 11
        if ($basePath !== null && \array_pop($basePath) !== $this->root) {
53
            throw new InvalidReferenceNodeException('Base node for pointer is not a child of the generator root node');
54
        }
55
    }
56
57
    /**
58
     * @param Node|Document $root
59
     * @throws InvalidReferenceNodeException
60
     */
61 14
    public function __construct($root)
62
    {
63 14
        if ($root instanceof Document) {
64 1
            $root = $root->getRootNode();
65
        }
66
67 14
        if (!($root instanceof Node)) {
68 1
            throw new InvalidReferenceNodeException(
69 1
                'Pointer generator root node must be instance of ' . Node::class . ' or ' . Document::class
70
            );
71
        }
72
73 13
        $this->root = $root;
74 13
        $this->rootParent = $root->getParent();
75 13
        $this->ownerDocument = $root->getOwnerDocument();
76
    }
77
78 2
    public function getRootNode(): Node
79
    {
80 2
        return $this->root;
81
    }
82
83
    /**
84
     * @throws InvalidSubjectNodeException
85
     */
86 4
    public function generateAbsolutePointer(Node $target): Pointer
87
    {
88
        try {
89 4
            $targetPath = $target->getAncestors($this->root);
90
91 4
            $this->validateAndRemoveNodePathRoots($targetPath);
92
93 4
            return Pointer::createFromParameters($this->nodePathToPointerPath($targetPath));
94
        } catch (InvalidSubjectNodeException $e) {
95
            throw $e;
96
        //@codeCoverageIgnoreStart
97
        } catch (\Exception $e) {
98
            /** @noinspection PhpInternalEntityUsedInspection */
99
            throw unexpected($e);
100
        }
101
        //@codeCoverageIgnoreEnd
102
    }
103
104
    /**
105
     * @throws InvalidSubjectNodeException
106
     * @throws InvalidReferenceNodeException
107
     */
108 7
    public function generateRelativePointer(Node $target, Node $base): Pointer
109
    {
110 7
        $targetPath = $target->getAncestors($this->root);
111 7
        $basePath = $base->getAncestors($this->root);
112
113 7
        $this->validateAndRemoveNodePathRoots($targetPath, $basePath);
114
115 7
        while (!empty($targetPath) && \end($targetPath) === \end($basePath)) {
116 2
            \array_pop($targetPath);
117 2
            \array_pop($basePath);
118
        }
119
120
        try {
121 7
            return Pointer::createFromParameters($this->nodePathToPointerPath($targetPath), \count($basePath));
122
        //@codeCoverageIgnoreStart
123
        } catch (\Exception $e) {
124
            /** @noinspection PhpInternalEntityUsedInspection */
125
            throw unexpected($e);
126
        }
127
        //@codeCoverageIgnoreEnd
128
    }
129
}
130