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   1117 use 5.006;
  22         73  
4              
5 22     22   121 use strict;
  22         46  
  22         500  
6 22     22   109 use warnings;
  22         51  
  22         1139  
7              
8             our $VERSION = '1.16';
9              
10 22     22   138 use Scalar::Util qw( blessed weaken );
  22         53  
  22         23658  
11              
12             sub new {
13 249     249 1 30693 my $class = shift;
14              
15 249 100       732 return $class->clone( @_ )
16             if blessed $class;
17              
18 247         486 my $self = bless {}, $class;
19              
20 247         683 $self->_init( @_ );
21              
22 247         535 return $self;
23             }
24              
25             sub _init {
26 247     247   319 my $self = shift;
27 247         403 my ($value) = @_;
28              
29             $self->{_parent} = $self->_null,
30 247         479 $self->{_children} = [];
31             $self->{_value} = $value,
32              
33 247         541 $self->{_meta} = {};
34              
35 247         434 return $self;
36             }
37              
38             sub _clone_self {
39 40     40   55 my $self = shift;
40 40 100       107 my $value = @_ ? shift : $self->value;
41              
42 40         133 return blessed($self)->new( $value );
43             }
44              
45             sub _clone_children {
46 18     18   29 my ($self, $clone) = @_;
47              
48 18 100       22 if ( my @children = @{$self->{_children}} ) {
  18         53  
49 6         17 $clone->add_child({}, map { $_->clone } @children );
  9         21  
50             }
51             }
52              
53             sub clone {
54 41     41 1 3231 my $self = shift;
55              
56 41 100       150 return $self->new(@_) unless blessed $self;
57              
58 40         106 my $clone = $self->_clone_self(@_);
59 40         115 $self->_clone_children($clone);
60              
61 40         166 return $clone;
62             }
63              
64             sub add_child {
65 187     187 1 259 my $self = shift;
66 187         364 my ( $options, @nodes ) = @_;
67              
68 187         290 for my $node ( @nodes ) {
69 219         447 $node->_set_parent( $self );
70             }
71              
72 187 100       371 if ( defined $options->{at} ) {
73 9 100       49 if ( $options->{at} ) {
74 7         11 splice @{$self->{_children}}, $options->{at}, 0, @nodes;
  7         20  
75             }
76             else {
77 2         6 unshift @{$self->{_children}}, @nodes;
  2         6  
78             }
79             }
80             else {
81 178         231 push @{$self->{_children}}, @nodes;
  178         384  
82             }
83              
84 187         355 return $self;
85             }
86              
87             sub remove_child {
88 21     21 1 37 my $self = shift;
89 21         43 my ($options, @indices) = @_;
90              
91 21         30 my @return;
92 21         69 for my $idx (sort { $b <=> $a } @indices) {
  5         21  
93 26         45 my $node = splice @{$self->{_children}}, $idx, 1;
  26         62  
94 26         70 $node->_set_parent( $node->_null );
95              
96 26         55 push @return, $node;
97             }
98              
99 21         65 return @return;
100             }
101              
102             sub parent {
103 16421     16421 1 20970 my $self = shift;
104 16421         38462 return $self->{_parent};
105             }
106              
107             sub _set_parent {
108 243     243   315 my $self = shift;
109              
110 243         384 $self->{_parent} = shift;
111 243         590 weaken( $self->{_parent} );
112              
113 243         392 return $self;
114             }
115              
116             sub children {
117 10770     10770 1 15184 my $self = shift;
118 10770 100       15248 if ( @_ ) {
119 50         98 my @idx = @_;
120 50         85 return @{$self->{_children}}[@idx];
  50         299  
121             }
122             else {
123 10720 100 66     28453 if ( caller->isa( __PACKAGE__ ) || $self->isa( scalar(caller) ) ) {
124 10673 50       15021 return wantarray ? @{$self->{_children}} : $self->{_children};
  10673         23788  
125             }
126             else {
127 47         89 return @{$self->{_children}};
  47         264  
128             }
129             }
130             }
131              
132             sub value {
133 201     201 1 2113 my $self = shift;
134 201         237 my $value = shift;
135 201 50       363 $self->{_value} = $value if (defined $value);
136              
137 201         508 return $self->{_value};
138             }
139              
140             sub set_value {
141 6     6 1 10 my $self = shift;
142              
143 6         13 $self->{_value} = $_[0];
144              
145 6         11 return $self;
146             }
147              
148             sub meta {
149 4     4 1 10 my $self = shift;
150 4         5 my $meta = shift;
151 4 50 66     19 $self->{_meta} = {%{$self->{_meta} }, %$meta} if ($meta && !blessed($meta) && ref($meta) eq 'HASH');
  1   66     4  
152              
153 4         15 return $self->{_meta};
154             }
155              
156             sub mirror {
157 24     24 1 34 my $self = shift;
158              
159 24         27 @{$self->{_children}} = reverse @{$self->{_children}};
  24         41  
  24         39  
160 24         30 $_->mirror for @{$self->{_children}};
  24         86  
161              
162 24         52 return $self;
163             }
164              
165 22     22   172 use constant PRE_ORDER => 1;
  22         55  
  22         2101  
166 22     22   150 use constant POST_ORDER => 2;
  22         51  
  22         1395  
167 22     22   138 use constant LEVEL_ORDER => 3;
  22         66  
  22         16371  
168              
169             sub traverse {
170 175     175 1 58436 my $self = shift;
171 175         226 my $order = shift;
172 175 100       341 $order = $self->PRE_ORDER unless $order;
173              
174 175 100       300 if ( wantarray ) {
175 141         171 my @list;
176              
177 141 100       314 if ( $order eq $self->PRE_ORDER ) {
    100          
    50          
178 79         113 @list = ($self);
179 79         88 push @list, map { $_->traverse( $order ) } @{$self->{_children}};
  70         153  
  79         131  
180             }
181             elsif ( $order eq $self->POST_ORDER ) {
182 54         65 @list = map { $_->traverse( $order ) } @{$self->{_children}};
  64         122  
  54         86  
183 54         82 push @list, $self;
184             }
185             elsif ( $order eq $self->LEVEL_ORDER ) {
186 8         15 my @queue = ($self);
187 8         18 while ( my $node = shift @queue ) {
188 36         47 push @list, $node;
189 36         37 push @queue, @{$node->{_children}};
  36         85  
190             }
191             }
192             else {
193 0         0 return $self->error( "traverse(): '$order' is an illegal traversal order" );
194             }
195              
196 141         367 return @list;
197             }
198             else {
199 34         44 my $closure;
200              
201 34 100       120 if ( $order eq $self->PRE_ORDER ) {
    100          
    100          
202 16         22 my $next_node = $self;
203 16         31 my @stack = ( $self );
204 16         20 my @next_idx = ( 0 );
205              
206             $closure = sub {
207 88     88   276 my $node = $next_node;
208 88 100       144 return unless $node;
209 72         84 $next_node = undef;
210              
211 72   100     210 while ( @stack && !$next_node ) {
212 72   100     211 while ( @stack && !exists $stack[0]->{_children}[ $next_idx[0] ] ) {
213 72         87 shift @stack;
214 72         174 shift @next_idx;
215             }
216              
217 72 100       132 if ( @stack ) {
218 56         82 $next_node = $stack[0]->{_children}[ $next_idx[0]++ ];
219 56         72 unshift @stack, $next_node;
220 56         171 unshift @next_idx, 0;
221             }
222             }
223              
224 72         112 return $node;
225 16         86 };
226             }
227             elsif ( $order eq $self->POST_ORDER ) {
228 8         16 my @stack = ( $self );
229 8         12 my @next_idx = ( 0 );
230 8         13 while ( @{ $stack[0]->{_children} } ) {
  20         39  
231 12         23 unshift @stack, $stack[0]->{_children}[0];
232 12         17 unshift @next_idx, 0;
233             }
234              
235             $closure = sub {
236 44     44   140 my $node = $stack[0];
237 44 100       73 return unless $node;
238              
239 36         44 shift @stack; shift @next_idx;
  36         39  
240 36         47 $next_idx[0]++;
241              
242 36   100     100 while ( @stack && exists $stack[0]->{_children}[ $next_idx[0] ] ) {
243 16         28 unshift @stack, $stack[0]->{_children}[ $next_idx[0] ];
244 16         46 unshift @next_idx, 0;
245             }
246              
247 36         55 return $node;
248 8         40 };
249             }
250             elsif ( $order eq $self->LEVEL_ORDER ) {
251 8         20 my @nodes = ($self);
252             $closure = sub {
253 44     44   133 my $node = shift @nodes;
254 44 100       73 return unless $node;
255 36         47 push @nodes, @{$node->{_children}};
  36         54  
256 36         54 return $node;
257 8         38 };
258             }
259             else {
260 2         11 return $self->error( "traverse(): '$order' is an illegal traversal order" );
261             }
262              
263 32         284 return $closure;
264             }
265             }
266              
267             sub _null {
268 376     376   1049 return Tree::Null->new;
269             }
270              
271             package Tree::Null;
272              
273             our $VERSION = '1.16';
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   1983 '""' => sub { return "" },
283 123     123   284 '0+' => sub { return 0 },
284 901     901   3453 'bool' => sub { return },
285 22         336 fallback => 1,
286 22     22   2648 ;
  22         2103  
287              
288             {
289             my $singleton = bless \my($x), __PACKAGE__;
290 377     377   1599 sub new { return $singleton }
291 1185     1185   2645 sub AUTOLOAD { return $singleton }
292 1     1   5 sub can { return sub { return $singleton } }
  2     2   663  
293             }
294              
295             # The null object can do anything
296             sub isa {
297 37     37   93027 my ($proto, $class) = @_;
298              
299 37 100       157 if ( $class =~ /^Tree(?:::.*)?$/ ) {
300 28         91 return 1;
301             }
302              
303 9         55 return $proto->SUPER::isa( $class );
304             }
305              
306             1;
307             __END__