Passed
Push — master ( 5abd13...fe603b )
by SignpostMarv
03:56
created

ModifyDaftNestedObjectTreeInsertAdjacent()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 53
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 24
nc 6
nop 3
dl 0
loc 53
ccs 25
cts 25
cp 1
crap 6
rs 8.7155
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
* Base daft objects.
4
*
5
* @author SignpostMarv
6
*/
7
declare(strict_types=1);
8
9
namespace SignpostMarv\DaftObject;
10
11
use BadMethodCallException;
12
use InvalidArgumentException;
13
use RuntimeException;
14
15
abstract class DaftWriteableObjectMemoryTree extends DaftObjectMemoryTree implements DaftNestedWriteableObjectTree
16
{
17
    const EXCEPTION_ARGUMENT_DID_NOT_RESOLVE_TO_A_LEAF_NODE =
18
        'Argument %u passed to %s() did not resolve to a leaf node!';
19
20 60
    public function ModifyDaftNestedObjectTreeInsert(
21
        DaftNestedWriteableObject $newLeaf,
22
        DaftNestedWriteableObject $referenceLeaf,
23
        bool $before = false,
24
        bool $above = null
25
    ) : DaftNestedWriteableObject {
26 60
        if ($newLeaf->GetId() === $referenceLeaf->GetId()) {
27 12
            throw new InvalidArgumentException('Cannot modify leaf relative to itself!');
28
        }
29
30 48
        if (true === $above) {
31 36
            $this->ModifyDaftNestedObjectTreeInsertAbove($newLeaf, $referenceLeaf);
32 32
        } elseif (false === $above) {
33 6
            $this->ModifyDaftNestedObjectTreeInsertBelow($newLeaf, $referenceLeaf);
34
        } else {
35 28
            $this->ModifyDaftNestedObjectTreeInsertAdjacent($newLeaf, $referenceLeaf, $before);
36
        }
37
38 48
        $this->RebuildTreeInefficiently();
39
40 48
        $newLeaf = $this->RecallDaftObject($newLeaf->GetId());
41
42 48
        if ( ! ($newLeaf instanceof DaftNestedWriteableObject)) {
43 12
            throw new RuntimeException('Could not retrieve leaf from tree after rebuilding!');
44
        }
45
46 36
        return $newLeaf;
47
    }
48
49 60
    public function ModifyDaftNestedObjectTreeInsertLoose(
50
        $leaf,
51
        $referenceId,
52
        bool $before = false,
53
        bool $above = null
54
    ) : DaftNestedWriteableObject {
55 60
        $leaf = $this->MaybeGetLeaf($leaf);
56
57 48
        $reference = $this->RecallDaftObject($referenceId);
58
59 48
        $leafIsObject = ($leaf instanceof DaftNestedWriteableObject);
60
61 48
        if ( ! is_null($leaf) && ($reference instanceof DaftNestedWriteableObject)) {
62 18
            return $this->ModifyDaftNestedObjectTreeInsert($leaf, $reference, $before, $above);
63 48
        } elseif ( ! $leafIsObject || ($referenceId !== $this->GetNestedObjectTreeRootId())) {
64 24
            throw new InvalidArgumentException(sprintf(
65 24
                self::EXCEPTION_ARGUMENT_DID_NOT_RESOLVE_TO_A_LEAF_NODE,
66 24
                ( ! $leafIsObject) ? 1 : 2,
67 24
                __METHOD__
68
            ));
69
        }
70
71
        /**
72
        * @var DaftNestedWriteableObject $leaf
73
        */
74 24
        $leaf = $leaf;
75
76 24
        return $this->ModifyDaftNestedObjectTreeInsertLooseIntoTree($leaf, $before, $above);
77
    }
78
79 8
    public function ModifyDaftNestedObjectTreeRemoveWithObject(
80
        DaftNestedWriteableObject $root,
81
        ? DaftNestedWriteableObject $replacementRoot
82
    ) : int {
83
        if (
84 8
            $this->CountDaftNestedObjectTreeWithObject($root, false, null) > 0 &&
85 8
            is_null($replacementRoot)
86
        ) {
87 2
            throw new BadMethodCallException('Cannot leave orphan objects in a tree');
88
        }
89
90 6
        $root = $this->StoreThenRetrieveFreshCopy($root);
91
92 6
        if ( ! is_null($replacementRoot)) {
93 4
            $this->ModifyDaftNestedObjectTreeRemoveWithObjectPrepareRemovalAndRebuild(
94 4
                $root,
95 4
                $replacementRoot
96
            );
97
        }
98
99 6
        $this->RemoveDaftObject($root);
100
101 6
        $this->RebuildTreeInefficiently();
102
103 6
        return $this->CountDaftNestedObjectFullTree();
104
    }
105
106
    /**
107
    * {@inheritdoc}
108
    */
109 12
    public function ModifyDaftNestedObjectTreeRemoveWithId($root, $replacementRoot) : int
110
    {
111 12
        $rootObject = $this->RecallDaftObject($root);
112
113 12
        if ($rootObject instanceof DaftNestedWriteableObject) {
114
            if (
115 12
                $this->CountDaftNestedObjectTreeWithObject($rootObject, false, null) > 0 &&
116 12
                is_null($replacementRoot)
117
            ) {
118 2
                throw new BadMethodCallException('Cannot leave orphan objects in a tree');
119
            } elseif (
120 10
                ! is_null($replacementRoot) &&
121 10
                $replacementRoot !== $this->GetNestedObjectTreeRootId()
122
            ) {
123 4
                return $this->MaybeRemoveWithPossibleObject(
124 4
                    $rootObject,
125 4
                    $this->RecallDaftObject($replacementRoot)
126
                );
127
            }
128
129 6
            $this->UpdateRemoveThenRebuild($rootObject, $replacementRoot);
130
        }
131
132 6
        return $this->CountDaftNestedObjectFullTree();
133
    }
134
135 4
    protected function ModifyDaftNestedObjectTreeRemoveWithObjectPrepareRemovalAndRebuild(
136
        DaftNestedWriteableObject $root,
137
        DaftNestedWriteableObject $replacementRoot
138
    ) : void {
139
        /**
140
        * @var scalar|scalar[] $replacementRootId
141
        */
142 4
        $replacementRootId = $this->StoreThenRetrieveFreshCopy($replacementRoot)->GetId();
143
144
        /**
145
        * @var DaftNestedWriteableObject $alter
146
        */
147 4
        foreach ($this->RecallDaftNestedObjectTreeWithObject($root, false, 1) as $alter) {
148 2
            $alter->AlterDaftNestedObjectParentId($replacementRootId);
149 2
            $this->StoreThenRetrieveFreshCopy($alter);
150
        }
151 4
    }
152
153
    /**
154
    * @param DaftNestedWriteableObject|mixed $leaf
155
    */
156 60
    protected function MaybeGetLeaf($leaf) : ? DaftNestedWriteableObject
157
    {
158 60
        if ($leaf === $this->GetNestedObjectTreeRootId()) {
159 12
            throw new InvalidArgumentException('Cannot pass root id as new leaf');
160 48
        } elseif ($leaf instanceof DaftNestedWriteableObject) {
161 24
            return $this->StoreThenRetrieveFreshCopy($leaf);
162
        }
163
164
        /**
165
        * @var DaftNestedWriteableObject|null $out
166
        */
167 40
        $out = $this->RecallDaftObject($leaf);
168
169 40
        return $out;
170
    }
171
172 24
    protected function ModifyDaftNestedObjectTreeInsertLooseIntoTree(
173
        DaftNestedWriteableObject $leaf,
174
        bool $before,
175
        ? bool $above
176
    ) : DaftNestedWriteableObject {
177 24
        $tree = $this->RecallDaftNestedObjectFullTree(0);
178
        $tree = array_filter($tree, function (DaftNestedWriteableObject $e) use ($leaf) : bool {
179 24
            return $e->GetId() !== $leaf->GetId();
180 24
        });
181
182 24
        if (0 === count($tree)) {
183 24
            $leaf->SetIntNestedLeft(0);
184 24
            $leaf->SetIntNestedRight(1);
185 24
            $leaf->SetIntNestedLevel(0);
186 24
            $leaf->AlterDaftNestedObjectParentId($this->GetNestedObjectTreeRootId());
187
188 24
            return $this->StoreThenRetrieveFreshCopy($leaf);
189
        }
190
191
        /**
192
        * @var DaftNestedWriteableObject $reference
193
        */
194 24
        $reference = $before ? current($tree) : end($tree);
195
196 24
        return $this->ModifyDaftNestedObjectTreeInsert($leaf, $reference, $before, $above);
197
    }
198
199 4
    protected function MaybeRemoveWithPossibleObject(
200
        DaftNestedWriteableObject $rootObject,
201
        ? DaftObject $replacementRootObject
202
    ) : int {
203 4
        if ( ! ($replacementRootObject instanceof DaftNestedWriteableObject)) {
204 2
            throw new InvalidArgumentException(
205 2
                'Could not locate replacement root, cannot leave orphan objects!'
206
            );
207
        }
208
209 2
        return $this->ModifyDaftNestedObjectTreeRemoveWithObject(
210 2
            $rootObject,
211 2
            $replacementRootObject
212
        );
213
    }
214
215
    /**
216
    * @param mixed $replacementRoot
217
    */
218 6
    protected function UpdateRemoveThenRebuild(
219
        DaftNestedWriteableObject $rootObject,
220
        $replacementRoot
221
    ) : void {
222
        /**
223
        * @var DaftNestedWriteableObject $alter
224
        */
225
        foreach (
226 6
            $this->RecallDaftNestedObjectTreeWithObject($rootObject, false, 1) as $alter
227
        ) {
228 2
            $alter = $this->StoreThenRetrieveFreshCopy($alter);
229 2
            $alter->AlterDaftNestedObjectParentId($replacementRoot);
230 2
            $this->RememberDaftObject($alter);
231
        }
232
233 6
        $this->RemoveDaftObject($rootObject);
234
235 6
        $this->RebuildTreeInefficiently();
236 6
    }
237
238 36
    protected function ModifyDaftNestedObjectTreeInsertAbove(
239
        DaftNestedWriteableObject $newLeaf,
240
        DaftNestedWriteableObject $referenceLeaf
241
    ) : void {
242 36
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
243 36
        $referenceLeaf->AlterDaftNestedObjectParentId($newLeaf->GetId());
244
245 36
        $this->StoreThenRetrieveFreshCopy($newLeaf);
246 36
        $this->StoreThenRetrieveFreshCopy($referenceLeaf);
247 36
    }
248
249 6
    protected function ModifyDaftNestedObjectTreeInsertBelow(
250
        DaftNestedWriteableObject $newLeaf,
251
        DaftNestedWriteableObject $referenceLeaf
252
    ) : void {
253 6
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->GetId());
254 6
        $this->StoreThenRetrieveFreshCopy($newLeaf);
255 6
    }
256
257 40
    protected function ModifyDaftNestedObjectTreeInsertAdjacent(
258
        DaftNestedWriteableObject $newLeaf,
259
        DaftNestedWriteableObject $referenceLeaf,
260
        bool $before
261
    ) : void {
262
        /**
263
        * @var array<int, DaftNestedWriteableObject> $siblings
264
        */
265 40
        $siblings = array_values(array_filter(
266 40
            $this->RecallDaftNestedObjectTreeWithId(
267 40
                $referenceLeaf->ObtainDaftNestedObjectParentId(),
268 40
                false,
269
                0
270
            ),
271
            function (DaftNestedWriteableObject $leaf) use ($newLeaf) : bool {
272 28
                return $leaf->GetId() !== $newLeaf->GetId();
273 40
            }
274
        ));
275
276 40
        $siblingIds = [];
277 40
        $siblingSort = [];
278 40
        $j = count($siblings);
279
280
        /**
281
        * @var DaftNestedWriteableObject $leaf
282
        */
283 40
        foreach ($siblings as $leaf) {
284
            /**
285
            * @var scalar|scalar[] $siblingId
286
            */
287 28
            $siblingId = $leaf->GetId();
288 28
            $siblingIds[] = $siblingId;
289 28
            $siblingSort[] = $leaf->GetIntNestedSortOrder();
290
        }
291
292 40
        $pos = array_search($referenceLeaf->GetId(), $siblingIds, true);
293
294 40
        if (false === $pos) {
295 12
            throw new RuntimeException('Reference leaf not found in siblings tree!');
296
        }
297
298 28
        for ($i = 0; $i < $j; ++$i) {
299 28
            $siblings[$i]->SetIntNestedSortOrder(
300 28
                $siblingSort[$i] +
301 28
                (($before ? ($i < $pos) : ($i <= $pos)) ? -1 : 1)
302
            );
303 28
            $this->StoreThenRetrieveFreshCopy($siblings[$i]);
304
        }
305
306 28
        $newLeaf->SetIntNestedSortOrder($siblingSort[$pos]);
307 28
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
308
309 28
        $this->StoreThenRetrieveFreshCopy($newLeaf);
310 28
    }
311
312 88
    protected function RememberDaftObjectData(DefinesOwnIdPropertiesInterface $object) : void
313
    {
314 88
        static::ThrowIfNotType($object, DaftNestedWriteableObject::class, 1, __METHOD__);
315
316 88
        parent::RememberDaftObjectData($object);
317 88
    }
318
319
    /**
320
    * @param DaftObject|string $object
321
    */
322 102
    protected static function ThrowIfNotType(
323
        $object,
324
        string $type,
325
        int $argument,
326
        string $function
327
    ) : void {
328 102
        if ( ! is_a($object, DaftNestedWriteableObject::class, is_string($object))) {
329 2
            throw new DaftObjectRepositoryTypeByClassMethodAndTypeException(
330 2
                $argument,
331 2
                static::class,
332 2
                $function,
333 2
                DaftNestedWriteableObject::class,
334 2
                is_string($object) ? $object : get_class($object)
335
            );
336
        }
337
338 100
        parent::ThrowIfNotType($object, $type, $argument, $function);
339 100
    }
340
341 48
    protected function RebuildTreeInefficiently() : void
342
    {
343
        $parentIdXref = [
344 48
            (array) $this->GetNestedObjectTreeRootId(),
345
        ];
346
347
        /**
348
        * @var array<int, array<int, DaftNestedWriteableObject>> $xRefChildren
349
        */
350
        $xRefChildren = [
351 48
            [],
352
        ];
353
354
        /**
355
        * @var array<int, scalar|scalar[]> $idXref
356
        */
357 48
        $idXref = [];
358
359 48
        $tree = $this->RecallDaftNestedObjectFullTree();
360
361
        usort($tree, function (DaftNestedWriteableObject $a, DaftNestedWriteableObject $b) : int {
362 48
            return $this->CompareObjects($a, $b);
363 48
        });
364
365
        /**
366
        * @var DaftNestedWriteableObject $leaf
367
        */
368 48
        foreach ($tree as $i => $leaf) {
369 48
            $leafParentId = $leaf->ObtainDaftNestedObjectParentId();
370 48
            $pos = array_search($leafParentId, $parentIdXref, true);
371
372 48
            if (false === $pos) {
373 42
                $parentIdXref[] = $leafParentId;
374
375
                /**
376
                * @var int $pos
377
                */
378 42
                $pos = array_search($leafParentId, $parentIdXref, true);
379
380 42
                $xRefChildren[$pos] = [];
381
            }
382
383 48
            if ( ! in_array($leaf, $xRefChildren[$pos], true)) {
384 48
                $xRefChildren[$pos][] = $leaf;
385
            }
386
387 48
            if ( ! in_array($leaf->GetId(), $idXref, true)) {
388
                /**
389
                * @var scalar|scalar[] $leafId
390
                */
391 48
                $leafId = $leaf->GetId();
392 48
                $idXref[] = $leafId;
393
            }
394
395 48
            $leaf->SetIntNestedLeft(0);
396 48
            $leaf->SetIntNestedRight(0);
397 48
            $leaf->SetIntNestedLevel(0);
398
399 48
            $tree[$i] = $this->StoreThenRetrieveFreshCopy($leaf);
400
        }
401
402 48
        $n = 0;
403
404
        /**
405
        * @var DaftNestedWriteableObject $rootLeaf
406
        */
407 48
        foreach ($xRefChildren[0] as $rootLeaf) {
408 48
            $n = $this->InefficientRebuild(
409 48
                $rootLeaf,
410 48
                0,
411 48
                $n,
412 48
                $parentIdXref,
413 48
                $idXref,
414 48
                $xRefChildren
415
            );
416
        }
417 48
    }
418
419 48
    protected function InefficientRebuild(
420
        DaftNestedWriteableObject $leaf,
421
        int $level,
422
        int $n,
423
        array $parentIds,
424
        array $ids,
425
        array $children
426
    ) : int {
427
        /**
428
        * @var scalar|scalar[] $id
429
        */
430 48
        $id = $leaf->GetId();
431
432 48
        $leaf->SetIntNestedLevel($level);
433 48
        $leaf->SetIntNestedLeft($n);
434
435 48
        ++$n;
436
437
        /**
438
        * @var int|false $parentPos
439
        */
440 48
        $parentPos = array_search((array) $id, $parentIds, true);
441
442 48
        if (false !== $parentPos) {
443
            /**
444
            * @var DaftNestedWriteableObject $childLeaf
445
            */
446 42
            foreach ($children[$parentPos] as $childLeaf) {
447 42
                $n = $this->InefficientRebuild(
448 42
                    $childLeaf,
449 42
                    $level + 1,
450 42
                    $n,
451 42
                    $parentIds,
452 42
                    $ids,
453 42
                    $children
454
                );
455
            }
456
        }
457
458 48
        $leaf->SetIntNestedRight($n);
459
460 48
        $this->StoreThenRetrieveFreshCopy($leaf);
461
462 48
        return $n + 1;
463
    }
464
465 50
    protected function StoreThenRetrieveFreshCopy(
466
        DaftNestedWriteableObject $leaf
467
    ) : DaftNestedWriteableObject {
468 50
        $this->RememberDaftObject($leaf);
469 50
        $this->ForgetDaftObject($leaf);
470 50
        $this->ForgetDaftObjectById($leaf->GetId());
471
472 50
        $fresh = $this->RecallDaftObject($leaf->GetId());
473
474 50
        if ( ! ($fresh instanceof DaftNestedWriteableObject)) {
475 2
            throw new RuntimeException('Was not able to obtain a fresh copy of the object!');
476
        }
477
478 48
        return $fresh;
479
    }
480
}
481