Completed
Pull Request — master (#24)
by Robbert
02:19
created

tree   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 243
Duplicated Lines 7.82 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 57.01%

Importance

Changes 11
Bugs 3 Features 4
Metric Value
wmc 33
c 11
b 3
f 4
lcom 1
cbo 4
dl 19
loc 243
ccs 61
cts 107
cp 0.5701
rs 9.4

11 Methods

Rating   Name   Duplication   Size   Complexity  
A collapse() 0 11 1
C expand() 0 36 7
B dive() 0 15 5
A parents() 0 10 2
A ls() 10 10 2
A search() 0 15 4
A map() 0 15 3
A reduce() 9 9 2
A filter() 0 8 2
A sort() 0 16 3
A getNodeName() 0 10 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/*
4
 * This file is part of the Ariadne Component Library.
5
 *
6
 * (c) Muze <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace arc;
13
14
/**
15
 *	Utility methods to handle common path related tasks, cleaning, changing relative to absolute, etc.
16
 */
17
class tree
18
{
19
    /**
20
     * Create a simple array from a tree. Each non-null nodeValue will be added with the path to its node as the key
21
     * @param  \arc\tree\Node $node
22
     * @param  string         $root
23
     * @param  string         $nodeName
24
     * @internal param \arc\tree\Node $tree
25
     * @return array          [ $path => $data, ... ]
26
     */
27 5
    public static function collapse($node, $root = '', $nodeName = 'nodeName')
28
    {
29 5
        return \arc\tree::map(
30 5
            $node,
31
            function ($child) {
32 5
                return $child->nodeValue;
33 5
            },
34 5
            $root,
35
            $nodeName
36 5
        );
37
    }
38
39
    /**
40
     * Creates a NamedNode tree from an array with path => nodeValue entries.
41
     * @param  array               $tree The collapsed tree: [ $path => $data, ... ]
42
     * @return \arc\tree\NamedNode an object tree with parent/children relations
43
     */
44 7
    public static function expand($tree = null)
45
    {
46 7
        if (is_object( $tree ) && isset( $tree->childNodes )) {
47
            return $tree; //FIXME: should we clone the tree to avoid shared state?
48
        }
49 7
        $root = new \arc\tree\NamedNode();
50 7
        if (!is_array($tree)) {
51 5
            return $root; // empty tree
52
        }
53 2
        $previousParent = $root;
54 2
        foreach ($tree as $path => $data) {
55 2
            $previousPath = $previousParent->getPath();
56 2
            $subPath = \arc\path::diff( $previousPath, $path );
57 2
            if ($subPath) {
58
                // create missing parent nodes, input tree may be sparsely filled
59 2
                $node = \arc\path::reduce(
60 2
                    $subPath,
61
                    function ($previous, $name) {
62 2
                        if ($name == '..') {
63 2
                            return $previous->parentNode;
64
                        }
65
66 2
                        return $previous->appendChild( $name );
67 2
                    },
68
                    $previousParent
69 2
                );
70 2
            } else {
71
                // means the previousParent is equal to the current path, e.g. the root
72
                $node = $previousParent;
73
            }
74 2
            $node->nodeValue = $data;
75 2
            $previousParent = $node;
76 2
        }
77
78 2
        return $root;
79
    }
80
81
    /**
82
     * Calls the first callback method on each successive parent until a non-null value is returned. Then
83
     * calls all the parents from that point back to this node with the second callback in reverse order.
84
     * The first callback (dive) must accept one parameter, the node.
85
     * The second callback (rise) must accept two parameters, the nde and the result up to that point.
86
     * @param  \arc\tree\Node $node         A tree node, must have traversable childNodes property and a parentNode property
87
     * @param  callable       $diveCallback The callback for the dive phase.
88
     * @param  callable       $riseCallback The callback for the rise phase.
89
     * @return mixed
90
     */
91 4
    public static function dive($node, $diveCallback = null, $riseCallback = null)
92
    {
93 4
        $result = null;
94 4
        if (is_callable( $diveCallback )) {
95 3
            $result = call_user_func( $diveCallback, $node );
96 3
        }
97 4
        if (!isset( $result ) && $node->parentNode) {
98 2
            $result = \arc\tree::dive( $node->parentNode, $diveCallback, $riseCallback );
99 2
        }
100 4
        if (is_callable( $riseCallback )) {
101 2
            return call_user_func( $riseCallback, $node, $result );
102
        } else {
103 3
            return $result;
104
        }
105
    }
106
107
    /**
108
     * Calls the callback method on each parent of the given node, starting at the root.
109
     * @param  \arc\tree\Node $node     A tree node, must have traversable childNodes property and a parentNode property
110
     * @param  callable       $callback The callback function applied to each parent.
111
     * @return mixed
112
     */
113 2
    public static function parents($node, $callback = null)
114
    {
115 2
        if (!isset( $callback )) {
116
            $callback = function ($node, $result) {
117
                return ( (array) $result ) + array( $node );
118
            };
119
        }
120
121 2
        return self::dive( $node, null, $callback );
122
    }
123
124
    /**
125
     * Calls the callback method on each of the direct child nodes of the given node.
126
     * @param  \arc\tree\Node $node
127
     * @param  callable       $callback The callback function applied to each child node
128
     * @param  mixed          $nodeName The name of the 'name' property or a function that returns the name of a node.
129
     * @return array
130
     */
131 View Code Duplication
    public static function ls($node, $callback, $nodeName = 'nodeName')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132
    {
133
        $result = [];
134
        foreach ($node->childNodes as $child) {
135
            $name = self::getNodeName( $child, $nodeName );
136
            $result[ $name ] = call_user_func( $callback, $child );
137
        }
138
139
        return $result;
140
    }
141
142
    /**
143
     * Calls the callback method on each child of the current node, including the node itself, until a non-null
144
     * result is returned. Returns that result. The tree is searched depth first.
145
     * @param  \arc\tree\Node $node
146
     * @param  callable       $callback The callback function applied to each child node
147
     * @return mixed
148
     */
149 2
    public static function search($node, $callback)
150 2
    {
151
        $result = call_user_func( $callback, $node );
152
        if (isset( $result )) {
153
            return $result;
154
        }
155
        foreach ($node->childNodes as $child) {
156
            $result = self::search( $child, $callback );
157
            if (isset( $result )) {
158
                return $result;
159
            }
160
        }
161
162
        return null;
163
    }
164
165
    /**
166
     * Calls the callback method on each child of the current node, including the node itself. Any non-null result
167
     * is added to the result array, with the path to the node as the key.
168
     * @param  \arc\tree\Node $node
169
     * @param  callable       $callback The callback function applied to each child node
170
     * @param  string         $root
171
     * @param  mixed          $nodeName The name of the 'name' property or a function that returns the name of a node.
172
     * @return array
173
     */
174 5
    public static function map($node, $callback, $root = '', $nodeName = 'nodeName')
175
    {
176 5
        $result = [];
177 5
        $name = self::getNodeName( $node, $nodeName );
178 5
        $path = $root . $name . '/';
179 5
        $callbackResult = call_user_func( $callback, $node );
180 5
        if (isset($callbackResult)) {
181 5
            $result[ $path ] = $callbackResult;
182 5
        }
183 5
        foreach ($node->childNodes as $child) {
184 5
            $result += self::map( $child, $callback, $path, $nodeName );
185 5
        }
186
187 5
        return $result;
188
    }
189
190
    /**
191
     * Calls the callback method on all child nodes of the given node, including the node itself. The result of each
192
     * call is passed on as the first argument to each succesive call.
193
     * @param  \arc\tree\Node $node
194
     * @param  callable       $callback
195
     * @param  mixed          $initial  The value to pass to the first callback call.
196
     * @return mixed
197
     */
198 View Code Duplication
    public static function reduce($node, $callback, $initial = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199
    {
200
        $result = call_user_func( $callback, $initial, $node );
201
        foreach ($node->childNodes as $child) {
202
            $result = self::reduce( $child, $callback, $result );
203
        }
204
205
        return $result;
206
    }
207
208
    /**
209
     * Filters the tree using a callback method. If the callback method returns true, the node's value is included
210
     * in the result, otherwise it is skipped. Filter returns a collapsed tree: [ path => nodeValue ]
211
     * The callback method must take one argument: the current node.
212
     * @param  \arc\tree\Node $node
213
     * @param  callable       $callback
214
     * @return array
215
     */
216
    public static function filter($node, $callback, $root = '', $nodeName = 'nodeName')
217
    {
218
        return self::map( $node, function ($node) use ($callback) {
219
            if (call_user_func( $callback, $node )) {
220
                return $node->nodeValue;
221
            }
222
        }, $root, $nodeName );
223
    }
224
225
    /**
226
     * Sorts the childNodes list of the node, recursively.
227
     * @param  \arc\tree\Node   $node
228
     * @param  callable         $callback
229
     * @param  mixed            $nodeName
230
     * @throws ExceptionDefault
231
     */
232
    public static function sort($node, $callback, $nodeName = 'nodeName')
233
    {
234
        if (is_array($node->childNodes)) {
235
            $sort = function ($node) use ($callback) {
236
                uasort( $node->childNodes, $callback );
237
            };
238
        } elseif ($node->childNodes instanceof \ArrayObject) {
239
            $sort = function ($node) use ($callback) {
240
                $node->childNodes->uasort( $callback );
241
            };
242
        } else {
243
            throw new \arc\ExceptionDefault( 'Cannot sort this tree - no suitable sort method found',
244
                \arc\exceptions::OBJECT_NOT_FOUND);
245
        }
246
        self::map( $node, $sort, '', $nodeName );
247
    }
248
249 5
    private static function getNodeName($node, $nodeName)
250
    {
251 5
        if (is_callable($nodeName)) {
252
            $name = call_user_func( $nodeName, $node );
253
        } else {
254 5
            $name = $node->{$nodeName};
255
        }
256
257 5
        return $name;
258
    }
259
}
260