Completed
Push — master ( 5d9a5d...e6d18b )
by Chris
02:53
created

Node   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Test Coverage

Coverage 26.92%

Importance

Changes 0
Metric Value
wmc 22
dl 0
loc 155
ccs 14
cts 52
cp 0.2692
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getAbsolutePointer() 0 11 2
B getRelativePointer() 0 22 4
A getNodePath() 0 9 2
A getFirstChild() 0 3 1
A getPointer() 0 5 2
A getKey() 0 3 1
A getParent() 0 3 1
A getLastChild() 0 3 1
A getPreviousSibling() 0 3 1
A getOwnerDocument() 0 3 1
A getNextSibling() 0 3 1
A createFromValue() 0 12 3
A hasChildren() 0 3 1
A __construct() 0 3 1
1
<?php declare(strict_types=1);
2
3
namespace DaveRandom\Jom;
4
5
use DaveRandom\Jom\Exceptions\InvalidNodeValueException;
6
use DaveRandom\Jom\Exceptions\InvalidPointerException;
7
use DaveRandom\Jom\Exceptions\InvalidSubjectNodeException;
8
9
abstract class Node implements \JsonSerializable
10
{
11
    protected $ownerDocument;
12
13
    /** @var string|int|null */
14
    protected $key;
15
16
    /** @var Node|null */
17
    protected $parent;
18
19
    /** @var Node|null */
20
    protected $previousSibling;
21
22
    /** @var Node|null */
23
    protected $nextSibling;
24
25
    /**
26
     * @return Node[]
27
     */
28
    private static function getNodePath(Node $node): array
29
    {
30
        $path = [$node];
31
32
        while (null !== $node = $node->parent) {
33
            $path[] = $node;
34
        }
35
36
        return $path;
37
    }
38
39
    /**
40
     * @throws InvalidPointerException
41
     */
42
    private function getAbsolutePointer(): Pointer
43
    {
44
        $current = $this;
45
        $components = [];
46
47
        while ($current->key !== null) {
48
            $components[] = $current->key;
49
            $current = $current->parent;
50
        }
51
52
        return new Pointer(\array_reverse($components), null, false);
53
    }
54
55
    /**
56
     * @throws InvalidSubjectNodeException
57
     * @throws InvalidPointerException
58
     */
59
    private function getRelativePointer(Node $base): Pointer
60
    {
61
        if ($base->ownerDocument !== $this->ownerDocument) {
62
            throw new InvalidSubjectNodeException('Base node belongs to a different document');
63
        }
64
65
        $thisPath = self::getNodePath($this);
66
        $basePath = self::getNodePath($base);
67
68
        // Find the nearest common ancestor
69
        while (\end($thisPath) === \end($basePath)) {
70
            \array_pop($thisPath);
71
            \array_pop($basePath);
72
        }
73
74
        $path = [];
75
76
        for ($i = \count($thisPath) - 1; $i >= 0; $i--) {
77
            $path[] = $thisPath[$i]->key;
78
        }
79
80
        return new Pointer($path, \count($basePath), false);
81
    }
82
83
    /**
84
     * @throws InvalidNodeValueException
85
     * @return static
86
     */
87 15
    public static function createFromValue($value, ?Document $ownerDocument = null): Node
88
    {
89 15
        static $nodeFactory;
90
91
        try {
92 15
            return ($nodeFactory ?? $nodeFactory = new UnsafeNodeFactory)
93 15
                ->createNodeFromValue($value, $ownerDocument);
94
        } catch (InvalidNodeValueException $e) {
95
            throw $e;
96
        //@codeCoverageIgnoreStart
97
        } catch (\Exception $e) {
98
            throw new \Error('Unexpected ' . \get_class($e) . ": {$e->getMessage()}", 0, $e);
99
        }
100
        //@codeCoverageIgnoreEnd
101
    }
102
103 68
    protected function __construct(?Document $ownerDocument)
104
    {
105 68
        $this->ownerDocument = $ownerDocument;
106
    }
107
108 6
    final public function getParent(): ?Node
109
    {
110 6
        return $this->parent;
111
    }
112
113 6
    final public function getPreviousSibling(): ?Node
114
    {
115 6
        return $this->previousSibling;
116
    }
117
118 6
    final public function getNextSibling(): ?Node
119
    {
120 6
        return $this->nextSibling;
121
    }
122
123
    public function hasChildren(): bool
124
    {
125
        return false;
126
    }
127
128
    public function getFirstChild(): ?Node
129
    {
130
        return null;
131
    }
132
133
    public function getLastChild(): ?Node
134
    {
135
        return null;
136
    }
137
138
    final public function getOwnerDocument(): ?Document
139
    {
140
        return $this->ownerDocument;
141
    }
142
143
    /**
144
     * @return string|int|null
145
     */
146 6
    final public function getKey()
147
    {
148 6
        return $this->key;
149
    }
150
151
    /**
152
     * @throws InvalidPointerException
153
     * @throws InvalidSubjectNodeException
154
     */
155
    final public function getPointer(Node $base = null): Pointer
156
    {
157
        return $base === null
158
            ? $this->getAbsolutePointer()
159
            : $this->getRelativePointer($base);
160
    }
161
162
    abstract public function getValue();
163
    abstract public function jsonSerialize();
164
}
165