LinkedList   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 376
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 47
eloc 122
dl 0
loc 376
rs 8.64
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A backward() 0 4 1
A first() 0 4 1
A seekHead() 0 4 1
A setValues() 0 4 1
A getValues() 0 3 1
A getSize() 0 3 1
A forward() 0 4 1
A tail() 0 4 1
A remove() 0 3 1
A get() 0 3 1
A last() 0 4 1
A set() 0 3 1
A push() 0 3 1
A seekTail() 0 4 1
A validIndex() 0 3 1
A insert() 0 4 2
A seek() 0 12 3
A pushAll() 0 8 2
A indexGuard() 0 5 2
A shift() 0 8 1
A insertBefore() 0 6 1
A insertAfter() 0 5 1
A clear() 0 10 1
A removeNode() 0 11 1
A __clone() 0 11 1
A unshift() 0 5 2
A seekTo() 0 10 3
A pop() 0 9 1
A copyFromContext() 0 12 2
A insertBetween() 0 14 1
A guardedSeek() 0 6 1
A intGuard() 0 7 2
A __construct() 0 11 1
A indexOf() 0 7 2
A toArray() 0 13 2

How to fix   Complexity   

Complex Class

Complex classes like LinkedList often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use LinkedList, and based on these observations, apply Extract Interface, too.

1
<?php namespace Mbh\Collection\Traits\Sequenceable;
2
3
use Mbh\Collection\Interfaces\Collection as CollectionInterface;
4
use Mbh\Collection\Interfaces\Functional as FunctionalInterface;
5
use Mbh\Collection\Interfaces\Sequenceable as SequenceableInterface;
6
use Mbh\Collection\Internal\Interfaces\LinkedNode;
7
use Mbh\Collection\Internal\LinkedDataNode;
8
use Mbh\Collection\Internal\LinkedTerminalNode;
9
use Mbh\Interfaces\Allocated as AllocatedInterface;
10
use Mbh\Traits\Capacity;
11
use Mbh\Traits\EmptyGuard;
12
use SplFixedArray;
13
use Traversable;
14
use OutOfBoundsException;
15
use Exception;
16
17
/**
18
 * MBHFramework
19
 *
20
 * @link      https://github.com/MBHFramework/mbh-framework
21
 * @copyright Copyright (c) 2017 Ulises Jeremias Cornejo Fandos
22
 * @license   https://github.com/MBHFramework/mbh-framework/blob/master/LICENSE (MIT License)
23
 */
24
25
trait LinkedList
26
{
27
    use LinkedList\ArrayAccess;
28
    use LinkedList\Countable;
29
    use LinkedList\Iterator;
30
31
    protected $head;
32
    protected $tail;
33
    protected $size = 0;
34
    protected $current;
35
    protected $offset = -1;
36
37
    /**
38
     * Create an fixed array
39
     *
40
     * @param array|Traversable $array data
41
     */
42
    public function __construct($array = [])
43
    {
44
        $this->head = $head = new LinkedTerminalNode();
45
        $this->tail = $tail = new LinkedTerminalNode();
46
47
        $head->setNext($tail);
48
        $tail->setPrev($head);
49
50
        $this->current = $this->head;
51
52
        $this->pushAll($array);
53
    }
54
55
    public function __clone()
56
    {
57
        $list = $this->copyFromContext($this->head->next());
58
59
        $this->head = $list->head;
60
        $this->tail = $list->tail;
61
62
        $this->current = $this->head;
63
        $this->offset = -1;
64
65
        $this->size = $list->size;
66
    }
67
68
    protected function backward()
69
    {
70
        $this->current = $this->current->prev();
71
        $this->offset--;
72
    }
73
74
    public function clear()
75
    {
76
        $this->head->setNext($this->tail);
77
        $this->tail->setPrev($this->head);
78
79
        $this->current = $this->head;
80
        $this->size = 0;
81
        $this->offset = -1;
82
83
        $this->capacity = self::MIN_CAPACITY;
0 ignored issues
show
Bug introduced by
The constant Mbh\Collection\Traits\Se...inkedList::MIN_CAPACITY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug Best Practice introduced by
The property capacity does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
84
    }
85
86
    protected function copyFromContext(LinkedNode $context)
87
    {
88
        $list = new static();
89
90
        for ($n = $context; $n !== $this->tail; $n = $n->next()) {
91
            /**
92
             * @var LinkedDataNode $n
93
             */
94
            $list->push($n->value());
95
        }
96
97
        return $list;
98
    }
99
100
    protected function forward()
101
    {
102
        $this->current = $this->current->next();
103
        $this->offset++;
104
    }
105
106
    public function first()
107
    {
108
        $this->emptyGuard(__METHOD__);
109
        return $this->seekHead()->value();
110
    }
111
112
    public function get(int $index)
113
    {
114
        return $this[$index];
115
    }
116
117
    protected function getSize()
118
    {
119
        return $this->size;
120
    }
121
122
    protected function getValues(): Traversable
123
    {
124
        return SplFixedArray::fromArray($this->toArray());
125
    }
126
127
    protected function guardedSeek($index, $method)
128
    {
129
        $index = $this->intGuard($index);
130
        $this->indexGuard($index, $method);
131
132
        return $this->seekTo($index);
133
    }
134
135
    protected function indexGuard($offset, $method)
136
    {
137
        if (!$this->offsetExists($offset)) {
138
            throw new OutOfBoundsException(
139
                "{$method} was called with invalid index: {$offset}"
140
            );
141
        }
142
    }
143
144
    /**
145
     * @param $value
146
     * @return int
147
     */
148
    public function indexOf($value)
149
    {
150
        if (($index = $this->search($value)) === null) {
151
            return -1;
152
        }
153
154
        return $index;
155
    }
156
157
    public function insert(int $index, ...$values)
158
    {
159
        foreach ($values as &$value) {
160
            $this->insertBefore($index++, $value);
161
        }
162
    }
163
164
    /**
165
     * @param int $position
166
     * @param mixed $value
167
     * @return void
168
     *
169
     * @throws OutOfBoundsException
170
     */
171
    public function insertAfter(int $position, $value)
172
    {
173
        $n = $this->guardedSeek($position, __METHOD__);
174
        $this->insertBetween($n, $n->next(), $value);
175
        $this->current = $this->current->prev();
176
    }
177
178
    /**
179
     * @param int $position
180
     * @param mixed $value
181
     * @return void
182
     *
183
     * @throws OutOfBoundsException
184
     */
185
    public function insertBefore(int $position, $value)
186
    {
187
        $n = $this->guardedSeek($position, __METHOD__);
188
        $this->insertBetween($n->prev(), $n, $value);
189
        $this->current = $this->current->next();
190
        $this->offset++;
191
    }
192
193
    protected function insertBetween(LinkedNode $a, LinkedNode $b, $value)
194
    {
195
        $n = new LinkedDataNode($value);
196
197
        $a->setNext($n);
198
        $b->setPrev($n);
199
200
        $n->setPrev($a);
201
        $n->setNext($b);
202
203
        $this->current = $n;
204
        $this->size++;
205
206
        $this->checkCapacity();
207
    }
208
209
    /**
210
     * @param mixed $i
211
     * @return int
212
     * @throws Exception
213
     */
214
    protected function intGuard($i)
215
    {
216
        if (filter_var($i, FILTER_VALIDATE_INT) === false) {
217
            throw new Exception;
218
        }
219
220
        return (int) $i;
221
    }
222
223
    public function last()
224
    {
225
        $this->emptyGuard(__METHOD__);
226
        return $this->seekTail()->value();
227
    }
228
229
    public function pop()
230
    {
231
        $this->emptyGuard(__METHOD__);
232
233
        $n = $this->seekTail();
234
        $this->removeNode($n);
235
236
        $this->checkCapacity();
237
        return $n->value();
238
    }
239
240
    public function push(...$values)
241
    {
242
        $this->pushAll($values);
243
    }
244
245
    protected function pushAll($values)
246
    {
247
        foreach ($values as $key => $value) {
248
            $this->insertBetween($this->tail->prev(), $this->tail, $value);
249
            $this->offset = $this->size - 1;
250
        }
251
252
        $this->checkCapacity();
253
    }
254
255
    public function remove(int $index)
256
    {
257
        $this->removeNode($this->guardedSeek($index, __METHOD__));
258
    }
259
260
    protected function removeNode(LinkedNode $n)
261
    {
262
        $prev = $n->prev();
263
        $next = $n->next();
264
265
        $prev->setNext($next);
266
        $next->setPrev($prev);
267
268
        $this->size--;
269
270
        $this->checkCapacity();
271
    }
272
273
    /**
274
     * @link http://php.net/manual/en/seekableiterator.seek.php
275
     * @param int $position
276
     * @return mixed
277
     * @throws OutOfBoundsException
278
     * @throws Exception
279
     */
280
    public function seek($position)
281
    {
282
        $index = $this->intGuard($position);
283
        $this->indexGuard($index, __METHOD__);
284
285
        if ($index === 0) {
286
            return $this->seekHead()->value();
287
        } elseif ($index === $this->size - 1) {
288
            return $this->seekTail()->value();
289
        }
290
291
        return $this->seekTo($index)->value();
292
    }
293
294
    /**
295
     * @return LinkedDataNode
296
     */
297
    protected function seekTail()
298
    {
299
        $this->offset = $this->size - 1;
300
        return $this->current = $this->tail->prev();
301
    }
302
303
    /**
304
     * @return LinkedDataNode
305
     */
306
    protected function seekHead()
307
    {
308
        $this->offset = 0;
309
        return $this->current = $this->head->next();
310
    }
311
312
    /**
313
     * @param $offset
314
     * @return LinkedDataNode
315
     */
316
    protected function seekTo($offset)
317
    {
318
        $n = abs($diff = $this->offset - $offset);
319
        $action = ($diff < 0) ? 'forward' : 'backward';
320
321
        for ($i = 0; $i < $n; $i++) {
322
            $this->$action();
323
        }
324
325
        return $this->current;
326
    }
327
328
    /**
329
     * @inheritDoc
330
     */
331
    public function set(int $index, $value)
332
    {
333
        $this->offsetSet($index, $value);
334
    }
335
336
    protected function setValues(Traversable $traversable)
337
    {
338
        $this->clear();
339
        $this->pushAll($traversable);
340
    }
341
342
    public function shift()
343
    {
344
        $this->emptyGuard(__METHOD__);
345
346
        $n = $this->seekHead();
347
        $this->removeNode($n);
348
349
        return $n->value();
350
    }
351
352
    /**
353
     * Extract the elements after the first of a list, which must be non-empty.
354
     * @return SequenceableInterface
355
     * @throws EmptyException
356
     */
357
    public function tail()
358
    {
359
        $this->emptyGuard(__METHOD__);
360
        return $this->copyFromContext($this->head->next()->next());
361
    }
362
363
    public function toArray(): array
364
    {
365
        $array = [];
366
        $context = $this->head->next();
367
368
        for ($n = $context; $n !== $this->tail; $n = $n->next()) {
369
            /**
370
             * @var LinkedDataNode $n
371
             */
372
            $array[] = $n->value();
373
        }
374
375
        return $array;
376
    }
377
378
    public function unshift(...$values)
379
    {
380
        foreach ($values as &$value) {
381
            $this->insertBetween($this->head, $this->head->next(), $value);
382
            $this->offset = 0;
383
        }
384
    }
385
386
    /**
387
     * @inheritDoc
388
     */
389
    protected function validIndex(int $index)
390
    {
391
        return $this->offsetExists($index);
392
    }
393
394
    abstract protected function checkCapacity();
395
396
    abstract protected function emptyGuard($method);
397
398
    abstract public function isEmpty(): bool;
399
400
    abstract public function search($value);
401
}
402