Completed
Push — master ( 9afc27...035c9e )
by Chris
04:26
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
     * Create an array of the node's path to root in reverse order, including the root node
29
     *
30
     * @return Node[]
31
     */
32 11
    private function getNodePath(Node $node): array
33
    {
34 11
        $path = [];
35
36
        do {
37 11
            $path[] = $node;
38 11
            $node = $node->getParent();
39 11
        } while ($node !== $this->rootParent && $node !== null);
40
41 11
        return $path;
42
    }
43
44
    /**
45
     * @param Node[] $nodePath
46
     * @return string[]
47
     */
48 11
    private function nodePathToPointerPath(array $nodePath): array
49
    {
50 11
        $result = [];
51
52 11
        for ($i = \count($nodePath) - 1; $i >= 0; $i--) {
53 7
            $result[] = $nodePath[$i]->getKey();
54
        }
55
56 11
        return $result;
57
    }
58
59
    /**
60
     * @throws InvalidSubjectNodeException
61
     * @throws InvalidReferenceNodeException
62
     */
63 11
    private function validateAndRemoveNodePathRoots(array &$targetPath, array &$basePath = null): void
64
    {
65 11
        if (\array_pop($targetPath) !== $this->root) {
66
            throw new InvalidSubjectNodeException('Target node for pointer is not a child of the generator root node');
67
        }
68
69 11
        if ($basePath !== null && \array_pop($basePath) !== $this->root) {
70
            throw new InvalidReferenceNodeException('Base node for pointer is not a child of the generator root node');
71
        }
72
    }
73
74
    /**
75
     * @param Node|Document $root
76
     * @throws InvalidReferenceNodeException
77
     */
78 14
    public function __construct($root)
79
    {
80 14
        if ($root instanceof Document) {
81 1
            $root = $root->getRootNode();
82
        }
83
84 14
        if (!($root instanceof Node)) {
1 ignored issue
show
introduced by
$root is always a sub-type of DaveRandom\Jom\Node.
Loading history...
85 1
            throw new InvalidReferenceNodeException(
86 1
                'Pointer generator root node must be instance of ' . Node::class . ' or ' . Document::class
87
            );
88
        }
89
90 13
        $this->root = $root;
91 13
        $this->rootParent = $root->getParent();
92 13
        $this->ownerDocument = $root->getOwnerDocument();
93
    }
94
95 2
    public function getRootNode(): Node
96
    {
97 2
        return $this->root;
98
    }
99
100
    /**
101
     * @throws InvalidSubjectNodeException
102
     */
103 4
    public function generateAbsolutePointer(Node $target): Pointer
104
    {
105
        try {
106 4
            $targetPath = self::getNodePath($target);
0 ignored issues
show
Bug Best Practice introduced by
The method DaveRandom\Jom\PointerGenerator::getNodePath() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

106
            /** @scrutinizer ignore-call */ 
107
            $targetPath = self::getNodePath($target);
Loading history...
107
108 4
            $this->validateAndRemoveNodePathRoots($targetPath);
109
110 4
            return Pointer::createFromParameters($this->nodePathToPointerPath($targetPath));
111
        } catch (InvalidSubjectNodeException $e) {
112
            throw $e;
113
        //@codeCoverageIgnoreStart
114
        } catch (\Exception $e) {
115
            throw unexpected($e);
116
        }
117
        //@codeCoverageIgnoreEnd
118
    }
119
120
    /**
121
     * @throws InvalidSubjectNodeException
122
     * @throws InvalidReferenceNodeException
123
     */
124 7
    public function generateRelativePointer(Node $target, Node $base): Pointer
125
    {
126 7
        $targetPath = self::getNodePath($target);
0 ignored issues
show
Bug Best Practice introduced by
The method DaveRandom\Jom\PointerGenerator::getNodePath() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

126
        /** @scrutinizer ignore-call */ 
127
        $targetPath = self::getNodePath($target);
Loading history...
127 7
        $basePath = self::getNodePath($base);
128
129 7
        $this->validateAndRemoveNodePathRoots($targetPath, $basePath);
130
131 7
        while (!empty($targetPath) && \end($targetPath) === \end($basePath)) {
132 2
            \array_pop($targetPath);
133 2
            \array_pop($basePath);
134
        }
135
136
        try {
137 7
            return Pointer::createFromParameters($this->nodePathToPointerPath($targetPath), \count($basePath));
138
        //@codeCoverageIgnoreStart
139
        } catch (\Exception $e) {
140
            throw unexpected($e);
141
        }
142
        //@codeCoverageIgnoreEnd
143
    }
144
}
145