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   535 use strict;
  3         6  
  3         348  
4              
5             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
6             our $DATE = '2021-10-07'; # DATE
7             our $DIST = 'Role-TinyCommons-Tree'; # DIST
8             our $VERSION = '0.128'; # 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   21 use Scalar::Util ();
  3         7  
  3         7120  
20              
21             # like children, but always return list
22             sub _children_as_list {
23 320     320   568 my $self = shift;
24 320         466 my @children;
25 320 50       641 if ($IGNORE_NO_CHILDREN_METHOD) {
26 320         472 eval {
27 320         808 @children = $self->$GET_CHILDREN_METHOD;
28             };
29 320 50       1913 return () if $@;
30             } else {
31 0         0 @children = $self->$GET_CHILDREN_METHOD;
32             }
33              
34 320 100       642 if (@children == 1) {
35 253 100       529 return () unless defined($children[0]);
36 217 100       528 return @{$children[0]} if ref($children[0]) eq 'ARRAY';
  208         565  
37             }
38 76         157 @children;
39             }
40              
41             # direct children first
42             sub _descendants {
43 80     80   136 my ($self, $res) = @_;
44 80         123 my @children = _children_as_list($self);
45 80         139 push @$res, @children;
46 80         312 for (@children) { _descendants($_, $res) }
  72         136  
47             }
48              
49             sub descendants {
50 8     8 1 27 my $self = shift;
51 8         19 my $res = [];
52 8         36 _descendants($self, $res);
53 8         42 @$res;
54             }
55              
56             sub _descendants_depth_first {
57 40     40   68 my ($self, $res) = @_;
58 40         66 my @children = _children_as_list($self);
59 40         77 for (@children) {
60 36         62 push @$res, $_;
61 36         82 _descendants_depth_first($_, $res);
62             }
63             }
64              
65             sub descendants_depth_first {
66 4     4 1 13 my $self = shift;
67 4         9 my $res = [];
68 4         15 _descendants_depth_first($self, $res);
69 4         55 @$res;
70             }
71              
72             sub ancestors {
73 9     9 1 1361 my $self = shift;
74 9         15 my @res;
75 9         37 my $parent = $self->$GET_PARENT_METHOD;
76 9         53 while ($parent) {
77 14         49 push @res, $parent;
78 14         38 $parent = $parent->$GET_PARENT_METHOD;
79             }
80 9         61 @res;
81             }
82              
83             sub retrieve_parent {
84 4     4 1 11 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 15 my ($self, $code) = @_;
97 4         10 for (descendants($self)) {
98 20 100       82 return $_ if $code->($_);
99             }
100 0         0 undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
101             }
102              
103             sub is_first_child {
104 12     12 1 26 my $self = shift;
105 12         45 my $parent = $self->$GET_PARENT_METHOD;
106 12 100       87 return 0 unless $parent;
107 8         31 my @siblings = _children_as_list($parent);
108 8 50       94 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[0]);
109             }
110              
111             sub is_last_child {
112 12     12 1 30 my $self = shift;
113 12         45 my $parent = $self->$GET_PARENT_METHOD;
114 12 100       69 return 0 unless $parent;
115 8         19 my @siblings = _children_as_list($parent);
116 8 50       71 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[-1]);
117             }
118              
119             sub is_only_child {
120 12     12 1 30 my $self = shift;
121 12         57 my $parent = $self->$GET_PARENT_METHOD;
122 12 100       103 return 0 unless $parent;
123 8         23 my @siblings = _children_as_list($parent);
124 8         42 @siblings==1;# && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[0]);
125             }
126              
127             sub is_nth_child {
128 12     12 1 27 my ($self, $n) = @_;
129 12         47 my $parent = $self->$GET_PARENT_METHOD;
130 12 50       63 return 0 unless $parent;
131 12         27 my @siblings = _children_as_list($parent);
132 12 50       140 @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         46 my $parent = $self->$GET_PARENT_METHOD;
138 12 50       63 return 0 unless $parent;
139 12         36 my @siblings = _children_as_list($parent);
140 12 50       144 @siblings >= $n && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[-$n]);
141             }
142              
143             sub is_first_child_of_type {
144 20     20 1 48 my $self = shift;
145 20         74 my $parent = $self->$GET_PARENT_METHOD;
146 20 50       101 return 0 unless $parent;
147 20         44 my $type = ref($self);
148 20         68 my @siblings = grep { ref($_) eq $type } _children_as_list($parent);
  100         221  
149 20 50       197 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[0]);
150             }
151              
152             sub is_last_child_of_type {
153 40     40 1 80 my $self = shift;
154 40         158 my $parent = $self->$GET_PARENT_METHOD;
155 40 50       190 return 0 unless $parent;
156 40         77 my $type = ref($self);
157 40         84 my @siblings = grep { ref($_) eq $type } _children_as_list($parent);
  200         429  
158 40 50       341 @siblings && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($siblings[-1]);
159             }
160              
161             sub is_only_child_of_type {
162 8     8 1 21 my $self = shift;
163 8         33 my $parent = $self->$GET_PARENT_METHOD;
164 8 50       42 return 0 unless $parent;
165 8         19 my $type = ref($self);
166 8         172 my @siblings = grep { ref($_) eq $type } _children_as_list($parent);
  40         152  
167 8         43 @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         60 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         176  
176 16 50       183 @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 42 my ($self, $n) = @_;
181 16         59 my $parent = $self->$GET_PARENT_METHOD;
182 16 50       86 return 0 unless $parent;
183 16         32 my $type = ref($self);
184 16         38 my @children = grep { ref($_) eq $type } _children_as_list($parent);
  80         173  
185 16 50       160 @children >= $n && Scalar::Util::refaddr($self) == Scalar::Util::refaddr($children[-$n]);
186             }
187              
188             sub prev_sibling {
189 12     12 1 31 my $self = shift;
190 12 50       53 my $parent = $self->$GET_PARENT_METHOD or return undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
191 12         67 my $refaddr = Scalar::Util::refaddr($self);
192 12         27 my @siblings = _children_as_list($parent);
193 12         38 for my $i (1..$#siblings) {
194 40 100       121 if (Scalar::Util::refaddr($siblings[$i]) == $refaddr) {
195 8         49 return $siblings[$i-1];
196             }
197             }
198 4         35 undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
199             }
200              
201             sub prev_siblings {
202 8     8 1 21 my $self = shift;
203 8 50       48 my $parent = $self->$GET_PARENT_METHOD or return ();
204 8         60 my $refaddr = Scalar::Util::refaddr($self);
205 8         22 my @siblings = _children_as_list($parent);
206 8         27 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         23 ();
212             }
213              
214             sub next_sibling {
215 12     12 1 26 my $self = shift;
216 12 50       54 my $parent = $self->$GET_PARENT_METHOD or return undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
217 12         70 my $refaddr = Scalar::Util::refaddr($self);
218 12         27 my @siblings = _children_as_list($parent);
219 12         41 for my $i (0..$#siblings-1) {
220 32 100       78 if (Scalar::Util::refaddr($siblings[$i]) == $refaddr) {
221 8         42 return $siblings[$i+1];
222             }
223             }
224 4         35 undef; ## no critic: Subroutines::ProhibitExplicitReturnUndef
225             }
226              
227             sub next_siblings {
228 8     8 1 20 my $self = shift;
229 8 50       40 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         26 for my $i (0..$#siblings-1) {
233 28 100       72 if (Scalar::Util::refaddr($siblings[$i]) == $refaddr) {
234 4         34 return @siblings[$i+1 .. $#siblings];
235             }
236             }
237 4         22 ();
238             }
239              
240             sub is_root {
241 4     4 1 11 my ($self, $n) = @_;
242 4         19 my $parent = $self->$GET_PARENT_METHOD;
243 4 100       37 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 11 my $self = shift;
267 4 50       26 my $parent = $self->$GET_PARENT_METHOD or return;
268 4         39 my $refaddr = Scalar::Util::refaddr($self);
269 4         10 my @remaining_siblings;
270 4         11 for my $sibling (_children_as_list($parent)) {
271 12 100       32 if (Scalar::Util::refaddr($sibling) == $refaddr) {
272 4         14 $sibling->$SET_PARENT_METHOD(undef);
273 4         19 next;
274             }
275 8         20 push @remaining_siblings, $sibling;
276             }
277 4         20 $parent->$SET_CHILDREN_METHOD(\@remaining_siblings);
278             }
279              
280             # check references
281             sub check {
282 10     10 1 26 my $self = shift;
283 10   100     41 my $opts = shift // {};
284              
285 10 100       31 if ($opts->{check_root}) {
286 4         16 my $parent = $self->$GET_PARENT_METHOD;
287 4 100       38 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         27 my $refaddr = Scalar::Util::refaddr($self);
292 8         12 my $i = 0;
293 8         19 for my $child (_children_as_list($self)) {
294 12         33 my $childs_parent = $child->$GET_PARENT_METHOD;
295 12 100 66     84 unless (defined $childs_parent &&
296             Scalar::Util::refaddr($childs_parent) == $refaddr) {
297 4         54 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       34 }) if $opts->{recurse};
303             }
304             }
305              
306              
307             1;
308             # ABSTRACT: Tree node routines
309              
310             __END__