File Coverage

blib/lib/Tree/Fast.pm
Criterion Covered Total %
statement 179 180 99.4
branch 44 48 91.6
condition 15 18 83.3
subroutine 35 35 100.0
pod 11 11 100.0
total 284 292 97.2


line stmt bran cond sub pod time code
1             package Tree::Fast;
2              
3 22     22   1068 use 5.006;
  22         67  
4              
5 22     22   116 use strict;
  22         35  
  22         482  
6 22     22   100 use warnings;
  22         36  
  22         1124  
7              
8             our $VERSION = '1.14';
9              
10 22     22   138 use Scalar::Util qw( blessed weaken );
  22         41  
  22         21423  
11              
12             sub new {
13 249     249 1 34983 my $class = shift;
14              
15 249 100       751 return $class->clone( @_ )
16             if blessed $class;
17              
18 247         448 my $self = bless {}, $class;
19              
20 247         699 $self->_init( @_ );
21              
22 247         504 return $self;
23             }
24              
25             sub _init {
26 247     247   336 my $self = shift;
27 247         408 my ($value) = @_;
28              
29             $self->{_parent} = $self->_null,
30 247         474 $self->{_children} = [];
31             $self->{_value} = $value,
32              
33 247         541 $self->{_meta} = {};
34              
35 247         459 return $self;
36             }
37              
38             sub _clone_self {
39 40     40   50 my $self = shift;
40 40 100       110 my $value = @_ ? shift : $self->value;
41              
42 40         135 return blessed($self)->new( $value );
43             }
44              
45             sub _clone_children {
46 18     18   30 my ($self, $clone) = @_;
47              
48 18 100       25 if ( my @children = @{$self->{_children}} ) {
  18         54  
49 6         14 $clone->add_child({}, map { $_->clone } @children );
  9         24  
50             }
51             }
52              
53             sub clone {
54 41     41 1 4408 my $self = shift;
55              
56 41 100       148 return $self->new(@_) unless blessed $self;
57              
58 40         104 my $clone = $self->_clone_self(@_);
59 40         120 $self->_clone_children($clone);
60              
61 40         130 return $clone;
62             }
63              
64             sub add_child {
65 187     187 1 267 my $self = shift;
66 187         331 my ( $options, @nodes ) = @_;
67              
68 187         311 for my $node ( @nodes ) {
69 219         451 $node->_set_parent( $self );
70             }
71              
72 187 100       375 if ( defined $options->{at} ) {
73 9 100       41 if ( $options->{at} ) {
74 7         10 splice @{$self->{_children}}, $options->{at}, 0, @nodes;
  7         20  
75             }
76             else {
77 2         5 unshift @{$self->{_children}}, @nodes;
  2         6  
78             }
79             }
80             else {
81 178         228 push @{$self->{_children}}, @nodes;
  178         368  
82             }
83              
84 187         383 return $self;
85             }
86              
87             sub remove_child {
88 21     21 1 48 my $self = shift;
89 21         48 my ($options, @indices) = @_;
90              
91 21         34 my @return;
92 21         71 for my $idx (sort { $b <=> $a } @indices) {
  5         24  
93 26         39 my $node = splice @{$self->{_children}}, $idx, 1;
  26         60  
94 26         80 $node->_set_parent( $node->_null );
95              
96 26         51 push @return, $node;
97             }
98              
99 21         60 return @return;
100             }
101              
102             sub parent {
103 16421     16421 1 21514 my $self = shift;
104 16421         38510 return $self->{_parent};
105             }
106              
107             sub _set_parent {
108 243     243   342 my $self = shift;
109              
110 243         395 $self->{_parent} = shift;
111 243         604 weaken( $self->{_parent} );
112              
113 243         405 return $self;
114             }
115              
116             sub children {
117 10770     10770 1 14799 my $self = shift;
118 10770 100       15207 if ( @_ ) {
119 50         95 my @idx = @_;
120 50         72 return @{$self->{_children}}[@idx];
  50         273  
121             }
122             else {
123 10720 100 66     28230 if ( caller->isa( __PACKAGE__ ) || $self->isa( scalar(caller) ) ) {
124 10673 50       15230 return wantarray ? @{$self->{_children}} : $self->{_children};
  10673         24673  
125             }
126             else {
127 47         67 return @{$self->{_children}};
  47         224  
128             }
129             }
130             }
131              
132             sub value {
133 201     201 1 9341 my $self = shift;
134 201         238 my $value = shift;
135 201 50       362 $self->{_value} = $value if (defined $value);
136              
137 201         519 return $self->{_value};
138             }
139              
140             sub set_value {
141 6     6 1 10 my $self = shift;
142              
143 6         12 $self->{_value} = $_[0];
144              
145 6         10 return $self;
146             }
147              
148             sub meta {
149 4     4 1 9 my $self = shift;
150 4         8 my $meta = shift;
151 4 50 66     20 $self->{_meta} = {%{$self->{_meta} }, %$meta} if ($meta && !blessed($meta) && ref($meta) eq 'HASH');
  1   66     5  
152              
153 4         12 return $self->{_meta};
154             }
155              
156             sub mirror {
157 24     24 1 34 my $self = shift;
158              
159 24         31 @{$self->{_children}} = reverse @{$self->{_children}};
  24         40  
  24         38  
160 24         33 $_->mirror for @{$self->{_children}};
  24         65  
161              
162 24         51 return $self;
163             }
164              
165 22     22   179 use constant PRE_ORDER => 1;
  22         49  
  22         2021  
166 22     22   142 use constant POST_ORDER => 2;
  22         39  
  22         1128  
167 22     22   124 use constant LEVEL_ORDER => 3;
  22         45  
  22         15775  
168              
169             sub traverse {
170 175     175 1 54961 my $self = shift;
171 175         231 my $order = shift;
172 175 100       341 $order = $self->PRE_ORDER unless $order;
173              
174 175 100       290 if ( wantarray ) {
175 141         164 my @list;
176              
177 141 100       308 if ( $order eq $self->PRE_ORDER ) {
    100          
    50          
178 79         121 @list = ($self);
179 79         95 push @list, map { $_->traverse( $order ) } @{$self->{_children}};
  70         140  
  79         123  
180             }
181             elsif ( $order eq $self->POST_ORDER ) {
182 54         60 @list = map { $_->traverse( $order ) } @{$self->{_children}};
  64         121  
  54         102  
183 54         71 push @list, $self;
184             }
185             elsif ( $order eq $self->LEVEL_ORDER ) {
186 8         15 my @queue = ($self);
187 8         21 while ( my $node = shift @queue ) {
188 36         45 push @list, $node;
189 36         39 push @queue, @{$node->{_children}};
  36         80  
190             }
191             }
192             else {
193 0         0 return $self->error( "traverse(): '$order' is an illegal traversal order" );
194             }
195              
196 141         348 return @list;
197             }
198             else {
199 34         46 my $closure;
200              
201 34 100       126 if ( $order eq $self->PRE_ORDER ) {
    100          
    100          
202 16         19 my $next_node = $self;
203 16         32 my @stack = ( $self );
204 16         29 my @next_idx = ( 0 );
205              
206             $closure = sub {
207 88     88   309 my $node = $next_node;
208 88 100       157 return unless $node;
209 72         94 $next_node = undef;
210              
211 72   100     203 while ( @stack && !$next_node ) {
212 72   100     225 while ( @stack && !exists $stack[0]->{_children}[ $next_idx[0] ] ) {
213 72         96 shift @stack;
214 72         177 shift @next_idx;
215             }
216              
217 72 100       139 if ( @stack ) {
218 56         86 $next_node = $stack[0]->{_children}[ $next_idx[0]++ ];
219 56         85 unshift @stack, $next_node;
220 56         158 unshift @next_idx, 0;
221             }
222             }
223              
224 72         129 return $node;
225 16         82 };
226             }
227             elsif ( $order eq $self->POST_ORDER ) {
228 8         20 my @stack = ( $self );
229 8         17 my @next_idx = ( 0 );
230 8         15 while ( @{ $stack[0]->{_children} } ) {
  20         48  
231 12         27 unshift @stack, $stack[0]->{_children}[0];
232 12         17 unshift @next_idx, 0;
233             }
234              
235             $closure = sub {
236 44     44   146 my $node = $stack[0];
237 44 100       77 return unless $node;
238              
239 36         49 shift @stack; shift @next_idx;
  36         48  
240 36         54 $next_idx[0]++;
241              
242 36   100     108 while ( @stack && exists $stack[0]->{_children}[ $next_idx[0] ] ) {
243 16         29 unshift @stack, $stack[0]->{_children}[ $next_idx[0] ];
244 16         48 unshift @next_idx, 0;
245             }
246              
247 36         58 return $node;
248 8         40 };
249             }
250             elsif ( $order eq $self->LEVEL_ORDER ) {
251 8         21 my @nodes = ($self);
252             $closure = sub {
253 44     44   146 my $node = shift @nodes;
254 44 100       82 return unless $node;
255 36         48 push @nodes, @{$node->{_children}};
  36         60  
256 36         56 return $node;
257 8         41 };
258             }
259             else {
260 2         13 return $self->error( "traverse(): '$order' is an illegal traversal order" );
261             }
262              
263 32         246 return $closure;
264             }
265             }
266              
267             sub _null {
268 376     376   1097 return Tree::Null->new;
269             }
270              
271             package Tree::Null;
272              
273             our $VERSION = '1.14';
274              
275             #XXX Add this in once it's been thought out
276             #our @ISA = qw( Tree );
277              
278             # You want to be able to interrogate the null object as to
279             # its class, so we don't override isa() as we do can()
280              
281             use overload
282 38     38   1943 '""' => sub { return "" },
283 123     123   277 '0+' => sub { return 0 },
284 901     901   3552 'bool' => sub { return },
285 22         333 fallback => 1,
286 22     22   2503 ;
  22         1882  
287              
288             {
289             my $singleton = bless \my($x), __PACKAGE__;
290 377     377   1621 sub new { return $singleton }
291 1185     1185   2704 sub AUTOLOAD { return $singleton }
292 1     1   5 sub can { return sub { return $singleton } }
  2     2   625  
293             }
294              
295             # The null object can do anything
296             sub isa {
297 39     39   66768 my ($proto, $class) = @_;
298              
299 39 100       152 if ( $class =~ /^Tree(?:::.*)?$/ ) {
300 28         92 return 1;
301             }
302              
303 11         59 return $proto->SUPER::isa( $class );
304             }
305              
306             1;
307             __END__