File Coverage

blib/lib/Code/Includable/Tree/NodeMethods.pm
Criterion Covered Total %
statement 159 164 96.9
branch 53 76 69.7
condition 4 5 80.0
subroutine 25 26 96.1
pod 21 21 100.0
total 262 292 89.7


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