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