Passed
Push — master ( 36bd6b...41f581 )
by stéphane
12:53
created

NodeGeneric::getTargetOnLessIndent()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 13.776

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 6
eloc 9
nc 4
nop 1
dl 0
loc 16
ccs 4
cts 10
cp 0.4
crap 13.776
rs 9.2222
c 1
b 1
f 0
1
<?php
2
3
namespace Dallgoot\Yaml\Nodes;
4
5
use Dallgoot\Yaml\NodeList;
6
7
/**
8
 * An abstract type for all Nodes that defines generic behaviour
9
 * Node deriving from this MUST implement the "build" method
10
 *
11
 * Note: custom var_dump output is defined by method "__debugInfo"
12
 *
13
 * @author  Stéphane Rebai <[email protected]>
14
 * @license Apache 2.0
15
 * @link    https://github.com/dallgoot/yaml
16
 */
17
abstract class NodeGeneric
18
{
19
    /** @var null|string|boolean */
20
    public $identifier;
21
    /** @var null|NodeGeneric */
22
    protected $_parent;
23
24
    /** @var int|null */
25
    public $indent = -1;
26
    /** @var int */
27
    public $line;
28
    /** @var null|string */
29
    public $raw;
30
    /** @var null|NodeGeneric|NodeList */
31
    public $value;
32
    /** @var string|null */
33
    public $anchor;
34
    /** @var string|null */
35
    public $tag;
36
37
    /**
38
     * Create the Node object and parses $nodeString
39
     *
40
     * @param string $nodeString The node string
41
     * @param int|null    $line       The line
42
     * @todo make it more permissive to tabs but replacement
43
     */
44 15
    public function __construct(string $nodeString, $line = 0)
45
    {
46 15
        $this->raw    = $nodeString;
47 15
        $this->line   = (int) $line;
48 15
        $nodeValue    = preg_replace("/^\t+/m", " ", $nodeString);
49 15
        $this->indent = strspn($nodeValue, ' ');
50 15
    }
51
52
    /**
53
     * Sets the parent of the current Node
54
     *
55
     * @param NodeGeneric $node The node
56
     *
57
     * @return NodeGeneric The currentNode
58
     */
59 8
    protected function setParent($node):NodeGeneric
60
    {
61 8
        $this->_parent = $node;
62 8
        return $this;
63
    }
64
65
    /**
66
     * Gets the ancestor with specified $indent or the direct $_parent
67
     *
68
     * @param int|null $indent The indent
69
     *
70
     * @return NodeGeneric   The parent.
71
     */
72 4
    public function getParent(int $indent = null):NodeGeneric
73
    {
74 4
        if (!is_int($indent)){
75 4
            if ($this->_parent instanceof NodeGeneric) {
76 3
                return $this->_parent;
77
            } else {
78 1
                throw new \Exception("Cannnot find a parent for ".get_class($this), 1);
79
            }
80
        }
81 2
        $cursor = $this->getParent();
82 2
        while (!($cursor instanceof Root)
83 2
                && (is_null($cursor->indent)
84 2
                || $cursor->indent >= $indent)) {
85 1
            if ($cursor->_parent) {
86 1
                $cursor = $cursor->_parent;
87
            } else {
88
                break;
89
            }
90
        }
91 2
        return $cursor;
92
    }
93
94
    /**
95
     * Gets the root of the structure map (or current Yaml document)
96
     *
97
     * @throws     \Exception  (description)
98
     *
99
     * @return     Root   The root node.
100
     */
101 2
    protected function getRoot():Root
102
    {
103 2
        if (is_null($this->_parent)) {
104 1
            throw new \Exception(__METHOD__.": can only be used when Node has a parent set", 1);
105
        }
106 1
        $pointer = $this;
107
        do {
108 1
            if ($pointer->_parent instanceof NodeGeneric) {
109 1
                $pointer = $pointer->_parent;
110
            } else {
111
                throw new \Exception("Node has no _parent set : ".get_class($pointer), 1);
112
            }
113 1
        } while (!($pointer instanceof Root));
114 1
        return $pointer;
115
    }
116
117
    /**
118
     * Set the value for the current Node :
119
     * - if value is null , then value = $child (Node)
120
     * - if value is Node, then value is a NodeList with (previous value AND $child)
121
     * - if value is a NodeList, push $child into
122
     *
123
     * @param NodeGeneric $child The child
124
     *
125
     * @return NodeGeneric
126
     */
127 7
    public function add(NodeGeneric $child):NodeGeneric
128
    {
129 7
        $child->setParent($this);
130 7
        if (is_null($this->value)) {
131 6
            $this->value = $child;
132
        } else {
133 5
            if ($this->value instanceof NodeGeneric) {
134 3
                $this->value = new NodeList($this->value);
135
            }
136 5
            $this->value->push($child);
137
        }
138 7
        return $child;
139
    }
140
141
    /**
142
     * Gets the deepest node.
143
     *
144
     * @return NodeGeneric  The deepest node.
145
     */
146 1
    public function getDeepestNode():NodeGeneric
147
    {
148 1
        $cursor = $this;
149 1
        while ($cursor->value instanceof NodeGeneric) {
150 1
            $cursor = $cursor->value;
151
        }
152 1
        return $cursor;
153
    }
154
155 1
    public function specialProcess(/** @scrutinizer ignore-unused */ NodeGeneric &$previous, /** @scrutinizer ignore-unused */ array &$emptyLines):bool
156
    {
157 1
        return false;
158
    }
159
160
   /**
161
     * Find parent target when current Node indentation is lesser than previous node indentation
162
     *
163
     * @param NodeGeneric $node
164
     *
165
     * @return NodeGeneric|Key
166
     */
167 1
    public function getTargetOnLessIndent(NodeGeneric &$node):NodeGeneric
168
    {
169 1
        $supposedParent = $this->getParent($node->indent);
170 1
        if ($node instanceof Item && $supposedParent instanceof Root) {
171
            if ($supposedParent->value->has('Key')) {
172
                $supposedParent->value->setIteratorMode(\SplDoublyLinkedList::IT_MODE_LIFO);
173
                foreach ($supposedParent->value as $child) {
174
                    if ($child instanceof Key) {
175
                        $supposedParent->value->setIteratorMode(\SplDoublyLinkedList::IT_MODE_FIFO);
176
                        // $supposedParent->value->rewind();
177
                        return $child;
178
                    }
179
                }
180
            }
181
        }
182 1
        return $supposedParent;
183
    }
184
185
    /**
186
     * Find parent target when current Node indentation is equal to previous node indentation
187
     *
188
     * @param NodeGeneric $node
189
     *
190
     * @return NodeGeneric
191
     */
192 1
    public function getTargetOnEqualIndent(NodeGeneric &$node):NodeGeneric
193
    {
194 1
        return $this->getParent();
195
    }
196
197
   /**
198
     * Find parent target when current Node indentation is superior than previous node indentation
199
     *
200
     * @param NodeGeneric $node
201
     *
202
     * @return NodeGeneric
203
     */
204
    public function getTargetOnMoreIndent(NodeGeneric &$node):NodeGeneric
205
    {
206
        return $this->isAwaitingChild($node) ? $this : $this->getParent();
207
    }
208
209 1
    protected function isAwaitingChild(NodeGeneric $node):bool
210
    {
211 1
        return false;
212
    }
213
214
    /**
215
     *
216
     * @param Array|Object|null         $parent The parent collector or NULL otherwise
217
     *
218
     * @return mixed  whatever the build process returns
219
     */
220
    public abstract function build(&$parent = null);
221
222
    /**
223
     * Determines if $subject is one of the Node types provided (as strings) in $comparison array
224
     * A node type is one of the class found in "nodes" folder.
225
     *
226
     * @param  string    ...$classNameList  A list of string whre each is a Node type e.g. 'Key', 'Blank', etc.
227
     *
228
     * @return     boolean  True if $subject is one of $comparison, False otherwise.
229
     */
230 6
    public function isOneOf(...$classNameList):bool
231
    {
232 6
        foreach ($classNameList as $className) {
233 6
            $fqn = __NAMESPACE__."\\$className";
234 6
            if ($this instanceof $fqn) return true;
235
        }
236 6
        return false;
237
    }
238
239
    /**
240
     * PHP internal function for debugging purpose : simplify output provided by 'var_dump'
241
     *
242
     * @return array  the Node properties and respective values displayed by 'var_dump'
243
     */
244 1
    public function __debugInfo():array
245
    {
246 1
        $props = [];
247 1
        $props['line->indent'] = "$this->line -> $this->indent";
248 1
        if ($this->identifier) $props['identifier'] = "($this->identifier)";
249 1
        if ($this->anchor)     $props['anchor']     = "($this->anchor)";
250 1
        if ($this->tag)        $props['tag']        = "($this->tag)";
251 1
        if ($this->value)      $props['value']      = $this->value;
252
        // $props['value'] = $this->value;
253 1
        $props['raw']   = $this->raw;
254 1
        if (!$this->_parent)  $props['parent'] = 'NO PARENT!!!';
255 1
        return $props;
256
    }
257
}
258