File Coverage

blib/lib/Code/Includable/Tree/NodeMethods.pm
Criterion Covered Total %
statement 164 178 92.1
branch 55 80 68.7
condition 4 5 80.0
subroutine 27 31 87.1
pod 26 26 100.0
total 276 320 86.2


line stmt bran cond sub pod time code
1             package Code::Includable::Tree::NodeMethods;
2              
3 3     3   503 use strict;
  3         8  
  3         358  
4              
5             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
6             our $DATE = '2021-10-07'; # DATE
7             our $DIST = 'RoleBundle-TinyCommons-Tree'; # DIST
8             our $VERSION = '0.129'; # VERSION
9              
10             our $IGNORE_NO_CHILDREN_METHOD = 1;
11              
12             our $GET_PARENT_METHOD = 'parent';
13             our $GET_CHILDREN_METHOD = 'children';
14             our $SET_PARENT_METHOD = 'parent';
15             our $SET_CHILDREN_METHOD = 'children';
16              
17             # we must contain no other functions
18              
19 3     3   23 use Scalar::Util ();
  3         5  
  3         7192  
20              
21             # like children, but always return list
22             sub _children_as_list {
23 320     320   505 my $self = shift;
24 320         522 my @children;
25 320 50       635 if ($IGNORE_NO_CHILDREN_METHOD) {
26 320         495 eval {
27 320         821 @children = $self->$GET_CHILDREN_METHOD;
28             };
29 320 50       1990 return () if $@;
30             } else {
31 0         0 @children = $self->$GET_CHILDREN_METHOD;
32             }
33              
34 320 100       727 if (@children == 1) {
35 250 100       504 return () unless defined($children[0]);
36 214 100       568 return @{$children[0]} if ref($children[0]) eq 'ARRAY';
  204         598  
37             }
38 80         183 @children;
39             }
40              
41             # direct children first
42             sub _descendants {
43 80     80   139 my ($self, $res) = @_;
44 80         146 my @children = _children_as_list($self);
45 80         139 push @$res, @children;
46 80         166 for (@children) { _descendants($_, $res) }
  72         132  
47             }
48              
49             sub descendants {
50 8     8 1 19 my $self = shift;
51 8         16 my $res = [];
52 8         31 _descendants($self, $res);
53 8         42 @$res;
54             }
55              
56             sub _descendants_depth_first {
57 40     40   95 my ($self, $res) = @_;
58 40         70 my @children = _children_as_list($self);
59 40         85 for (@children) {
60 36         72 push @$res, $_;
61 36         73 _descendants_depth_first($_, $res);
62             }
63             }
64              
65             sub descendants_depth_first {
66 4     4 1 10 my $self = shift;
67 4         10 my $res = [];
68 4         14 _descendants_depth_first($self, $res);
69 4         70 @$res;
70             }
71              
72             sub ancestors {
73 9     9 1 1432 my $self = shift;
74 9         17 my @res;
75 9         39 my $parent = $self->$GET_PARENT_METHOD;
76 9         55 while ($parent) {
77 14         51 push @res, $parent;
78 14         37 $parent = $parent->$GET_PARENT_METHOD;
79             }
80 9         79 @res;
81             }
82              
83             sub retrieve_parent {
84 4     4 1 13 my $self = shift;
85 4         25 $self->$GET_PARENT_METHOD;
86             }
87              
88             sub walk {
89 0     0 1 0 my ($self, $code) = @_;
90 0         0 for (descendants($self)) {
91 0         0 $code->($_);
92             }
93             }
94              
95             sub first_node {
96 4     4 1 14 my ($self, $code) = @_;
97 4         13 for (descendants($self)) {
98 20 100       91 return $_ if $code->($_);
99             }
100 0         0 undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
101             }
102              
103             sub is_first_child {
104 12     12 1 29 my $self = shift;
105 12         50 my $parent = $self->$GET_PARENT_METHOD;
106 12 100       77 return 0 unless $parent;
107 8         38 my @siblings = _children_as_list($parent);
108 8 50       83 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[0]);
109             }
110              
111             sub is_last_child {
112 12     12 1 29 my $self = shift;
113 12         45 my $parent = $self->$GET_PARENT_METHOD;
114 12 100       83 return 0 unless $parent;
115 8         20 my @siblings = _children_as_list($parent);
116 8 50       76 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[-1]);
117             }
118              
119             sub is_only_child {
120 12     12 1 39 my $self = shift;
121 12         45 my $parent = $self->$GET_PARENT_METHOD;
122 12 100       102 return 0 unless $parent;
123 8         22 my @siblings = _children_as_list($parent);
124 8         41 @siblings==1;# && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[0]);
125             }
126              
127             sub is_nth_child {
128 12     12 1 32 my ($self, $n) = @_;
129 12         46 my $parent = $self->$GET_PARENT_METHOD;
130 12 50       65 return 0 unless $parent;
131 12         27 my @siblings = _children_as_list($parent);
132 12 50       160 @siblings >= $n && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[$n-1]);
133             }
134              
135             sub is_nth_last_child {
136 12     12 1 30 my ($self, $n) = @_;
137 12         50 my $parent = $self->$GET_PARENT_METHOD;
138 12 50       64 return 0 unless $parent;
139 12         30 my @siblings = _children_as_list($parent);
140 12 50       185 @siblings >= $n && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[-$n]);
141             }
142              
143             sub is_first_child_of_type {
144 20     20 1 46 my $self = shift;
145 20         74 my $parent = $self->$GET_PARENT_METHOD;
146 20 50       109 return 0 unless $parent;
147 20         43 my $type = ref($self);
148 20         69 my @siblings = grep { ref($_) eq $type } _children_as_list($parent);
  100         234  
149 20 50       562 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[0]);
150             }
151              
152             sub is_last_child_of_type {
153 40     40 1 86 my $self = shift;
154 40         146 my $parent = $self->$GET_PARENT_METHOD;
155 40 50       199 return 0 unless $parent;
156 40         89 my $type = ref($self);
157 40         89 my @siblings = grep { ref($_) eq $type } _children_as_list($parent);
  200         450  
158 40 50       372 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[-1]);
159             }
160              
161             sub is_only_child_of_type {
162 8     8 1 20 my $self = shift;
163 8         43 my $parent = $self->$GET_PARENT_METHOD;
164 8 50       45 return 0 unless $parent;
165 8         18 my $type = ref($self);
166 8         60 my @siblings = grep { ref($_) eq $type } _children_as_list($parent);
  40         254  
167 8         46 @siblings == 1; # && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[0]);
168             }
169              
170             sub is_nth_child_of_type {
171 16     16 1 40 my ($self, $n) = @_;
172 16         74 my $parent = $self->$GET_PARENT_METHOD;
173 16 50       80 return 0 unless $parent;
174 16         33 my $type = ref($self);
175 16         39 my @siblings = grep { ref($_) eq $type } _children_as_list($parent);
  80         184  
176 16 50       171 @siblings >= $n && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[$n-1]);
177             }
178              
179             sub is_nth_last_child_of_type {
180 16     16 1 47 my ($self, $n) = @_;
181 16         61 my $parent = $self->$GET_PARENT_METHOD;
182 16 50       87 return 0 unless $parent;
183 16         35 my $type = ref($self);
184 16         38 my @children = grep { ref($_) eq $type } _children_as_list($parent);
  80         230  
185 16 50       166 @children >= $n && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($children[-$n]);
186             }
187              
188             sub prev_sibling {
189 12     12 1 29 my $self = shift;
190 12 50       55 my $parent = $self->$GET_PARENT_METHOD or return undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
191 12         84 my $refaddr = Scalar::Util::refaddr($self);
192 12         29 my @siblings = _children_as_list($parent);
193 12         41 for my $i (1..$#siblings) {
194 40 100       105 if (Scalar::Util::refaddr($siblings[$i]) == $refaddr) {
195 8         59 return $siblings[$i-1];
196             }
197             }
198 4         35 undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
199             }
200              
201             sub prev_siblings {
202 8     8 1 20 my $self = shift;
203 8 50       40 my $parent = $self->$GET_PARENT_METHOD or return ();
204 8         49 my $refaddr = Scalar::Util::refaddr($self);
205 8         22 my @siblings = _children_as_list($parent);
206 8         29 for my $i (1..$#siblings) {
207 24 100       65 if (Scalar::Util::refaddr($siblings[$i]) == $refaddr) {
208 4         37 return @siblings[0..$i-1];
209             }
210             }
211 4         21 ();
212             }
213              
214             sub next_sibling {
215 12     12 1 28 my $self = shift;
216 12 50       56 my $parent = $self->$GET_PARENT_METHOD or return undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
217 12         71 my $refaddr = Scalar::Util::refaddr($self);
218 12         44 my @siblings = _children_as_list($parent);
219 12         55 for my $i (0..$#siblings-1) {
220 32 100       83 if (Scalar::Util::refaddr($siblings[$i]) == $refaddr) {
221 8         46 return $siblings[$i+1];
222             }
223             }
224 4         20 undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
225             }
226              
227             sub next_siblings {
228 8     8 1 21 my $self = shift;
229 8 50       42 my $parent = $self->$GET_PARENT_METHOD or return ();
230 8         49 my $refaddr = Scalar::Util::refaddr($self);
231 8         20 my @siblings = _children_as_list($parent);
232 8         29 for my $i (0..$#siblings-1) {
233 28 100       71 if (Scalar::Util::refaddr($siblings[$i]) == $refaddr) {
234 4         43 return @siblings[$i+1 .. $#siblings];
235             }
236             }
237 4         21 ();
238             }
239              
240             sub is_root {
241 4     4 1 14 my ($self, $n) = @_;
242 4         18 my $parent = $self->$GET_PARENT_METHOD;
243 4 100       52 return $parent ? 0:1;
244             }
245              
246             sub has_min_children {
247 0     0 1 0 my ($self, $m) = @_;
248 0         0 my @children = _children_as_list($self);
249 0         0 @children >= $m;
250             }
251              
252             sub has_max_children {
253 0     0 1 0 my ($self, $n) = @_;
254 0         0 my @children = _children_as_list($self);
255 0         0 @children <= $n;
256             }
257              
258             sub has_children_between {
259 0     0 1 0 my ($self, $m, $n) = @_;
260 0         0 my @children = _children_as_list($self);
261 0 0       0 @children >= $m && @children <= $n;
262             }
263              
264             # remove self from parent
265             sub remove {
266 4     4 1 9 my $self = shift;
267 4 50       23 my $parent = $self->$GET_PARENT_METHOD or return;
268 4         28 my $refaddr = Scalar::Util::refaddr($self);
269 4         7 my @remaining_siblings;
270 4         11 for my $sibling (_children_as_list($parent)) {
271 12 100       34 if (Scalar::Util::refaddr($sibling) == $refaddr) {
272 4         16 $sibling->$SET_PARENT_METHOD(undef);
273 4         17 next;
274             }
275 8         17 push @remaining_siblings, $sibling;
276             }
277 4         18 $parent->$SET_CHILDREN_METHOD(\@remaining_siblings);
278             }
279              
280             # check references
281             sub check {
282 10     10 1 20 my $self = shift;
283 10   100     40 my $opts = shift // {};
284              
285 10 100       28 if ($opts->{check_root}) {
286 4         19 my $parent = $self->$GET_PARENT_METHOD;
287 4 100       37 defined $parent and die "check: parent is not undef";
288             }
289              
290             # check that all children refers back to me in their parent
291 8         25 my $refaddr = Scalar::Util::refaddr($self);
292 8         14 my $i = 0;
293 8         20 for my $child (_children_as_list($self)) {
294 12         36 my $childs_parent = $child->$GET_PARENT_METHOD;
295 12 100 66     135 unless (defined $childs_parent &&
296             Scalar::Util::refaddr($childs_parent) == $refaddr) {
297 4         63 die "check: Child #$i of $self does not refer back to its parent";
298             }
299             check($child, {
300             recurse=>1,
301             #check_root=>0,
302 8 50       40 }) if $opts->{recurse};
303             }
304             }
305              
306              
307             1;
308             # ABSTRACT: Tree node routines
309              
310             __END__