File Coverage

blib/lib/Data/Locations.pm
Criterion Covered Total %
statement 333 348 95.6
branch 70 98 71.4
condition 16 45 35.5
subroutine 32 34 94.1
pod 0 9 0.0
total 451 534 84.4


line stmt bran cond sub pod time code
1              
2             ###############################################################################
3             ## ##
4             ## Copyright (c) 1997 - 2009 by Steffen Beyer. ##
5             ## All rights reserved. ##
6             ## ##
7             ## This package is free software; you can redistribute it ##
8             ## and/or modify it under the same terms as Perl itself. ##
9             ## ##
10             ###############################################################################
11              
12             package Data::Locations;
13              
14 19     19   28689 use 5.004;
  19         78  
  19         1051  
15 19     19   107 use strict;
  19         29  
  19         1217  
16 19     19   115 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
  19         34  
  19         18120  
17              
18             require Carp;
19             require Symbol;
20             require Exporter;
21             require DynaLoader;
22              
23             @ISA = qw(Exporter DynaLoader);
24              
25             @EXPORT = ();
26              
27             @EXPORT_OK = ();
28              
29             %EXPORT_TAGS = (all => [@EXPORT_OK]);
30              
31             $VERSION = "5.5";
32              
33             bootstrap Data::Locations $VERSION;
34              
35             my $Class = __PACKAGE__; ## This class's name
36             my $Table = $Class . '::'; ## This class's symbol table
37              
38             my $Count = 0; ## Counter for generating unique names for all locations
39             my $Alive = 1; ## Flag for disabling auto-dump during global destruction
40              
41             *print = \&PRINT; ## Define public aliases for internal methods
42             *printf = \&PRINTF;
43             *read = \&READLINE;
44              
45             sub _usage_
46             {
47 0     0   0 my($text) = @_;
48              
49 0         0 Carp::croak("Usage: $text");
50             }
51              
52             sub _error_
53             {
54 3     3   6 my($name,$text) = @_;
55              
56 3         651 Carp::croak("${Table}${name}(): $text");
57             }
58              
59             sub _alert_
60             {
61 2     2   3 my($name,$text) = @_;
62              
63 2 50       446 Carp::carp("${Table}${name}(): $text") if $^W;
64             }
65              
66             sub _check_filename_
67             {
68 111     111   191 my($name,$file) = @_;
69              
70 111 50       275 if (defined $file)
71             {
72 111 50       269 if (ref($file))
73             {
74 0         0 &_error_($name, "reference not allowed as filename");
75             }
76             else
77             {
78 111 100       548 if ($file !~ /^\s*$/) { return "$file"; }
  25         101  
79             }
80             }
81 86         177 return '';
82             }
83              
84             sub new
85             {
86 84 50 33 84 0 3020 &_usage_("\$[top|sub]location = [$Class|\$location]->new( [ \$filename ] );")
87             if ((@_ < 1) || (@_ > 2));
88              
89 84         165 my($outer) = shift;
90 84         219 my($file,$name,$inner);
91              
92 84         132 $file = '';
93 84 100       266 $file = shift if (@_ > 0);
94 84         226 $file = &_check_filename_('new', $file);
95              
96 84         238 $name = 'LOCATION' . $Count++; ## Generate a unique name
97 19     19   131 no strict "refs";
  19         36  
  19         865  
98 84         109 $inner = \*{$Table . $name}; ## Create a reference of glob value
  84         723  
99 19     19   266 use strict "refs";
  19         39  
  19         117587  
100 84         208 bless($inner, $Class); ## Bless glob to become an object
101 84         118 tie(*{$inner}, $Class, $inner); ## Tie glob to itself
  84         781  
102 84         121 ${*{$inner}} = $inner; ## Use $ slot of glob for self-ref
  84         534  
  84         236  
103 84         139 @{*{$inner}} = (); ## Use @ slot of glob for the data
  84         89  
  84         237  
104 84         129 %{*{$inner}} = (); ## Use % slot of glob for obj attributes
  84         94  
  84         272  
105              
106 84         116 ${*{$inner}}{'name'} = $name; ## Also keep symbolic self-ref
  84         89  
  84         248  
107 84         126 ${*{$inner}}{'file'} = $file; ## Store filename (is auto-dump flag)
  84         92  
  84         231  
108              
109 84         134 ${*{$inner}}{'outer'} = {}; ## List of surrounding locations
  84         86  
  84         192  
110 84         121 ${*{$inner}}{'inner'} = {}; ## List of embedded locations
  84         96  
  84         171  
111              
112             ## Enable destruction when last user ref goes out of scope:
113              
114 84         312 ${*{$inner}}{'refs'} = &_mortalize_($inner);
  84         115  
  84         230  
115              
116 84 100       242 if (ref($outer)) ## Object method (or else class method)
117             {
118 39         62 ${${*{$inner}}{'outer'}}{${*{$outer}}{'name'}} = 1;
  39         47  
  39         804  
  39         45  
  39         43  
  39         825  
119 39         76 ${${*{$outer}}{'inner'}}{${*{$inner}}{'name'}} = 1;
  39         54  
  39         107  
  39         45  
  39         45  
  39         103  
120 39         73 push(@{*{$outer}}, $inner);
  39         45  
  39         92  
121             }
122 84         263 return $inner;
123             }
124              
125             sub TIEHANDLE
126             {
127 100     100   420 return $_[1];
128             }
129              
130             sub CLOSE
131             {
132 0     0   0 &_alert_("close", "operation ignored");
133             }
134              
135             sub _unlink_outer_
136             {
137 4     4   7 my($inner) = @_;
138 4         7 my($name,$list,$item);
139              
140 4         6 $name = ${*{$inner}}{'name'};
  4         6  
  4         12  
141 4         6 $list = ${*{$inner}}{'outer'};
  4         7  
  4         953  
142 4         7 foreach $item (keys %{$list})
  4         15  
143             {
144 0 0       0 if (exists $Data::Locations::{$item})
145             {
146 0         0 delete ${${*{ $Data::Locations::{$item} }}{'inner'}}{$name};
  0         0  
  0         0  
  0         0  
147             }
148             }
149 4         54 ${*{$inner}}{'outer'} = {};
  4         5  
  4         16  
150             }
151              
152             sub _unlink_inner_
153             {
154 11     11   26 my($outer) = @_;
155 11         17 my($name,$list,$item);
156              
157 11         15 $name = ${*{$outer}}{'name'};
  11         22  
  11         30  
158 11         19 $list = ${*{$outer}}{'inner'};
  11         12  
  11         28  
159 11         18 foreach $item (keys %{$list})
  11         38  
160             {
161 11 50       134 if (exists $Data::Locations::{$item})
162             {
163 11         15 delete ${${*{ $Data::Locations::{$item} }}{'outer'}}{$name};
  11         13  
  11         18  
  11         65  
164             }
165             }
166 11         22 ${*{$outer}}{'inner'} = {};
  11         15  
  11         41  
167             }
168              
169             sub delete
170             {
171 7 50 33 7 0 330 &_usage_('$location->delete();')
172             if ((@_ != 1) || !ref($_[0]));
173              
174 7         14 my($location) = @_;
175              
176 7         23 &_unlink_inner_($location);
177 7         12 delete ${*{$location}}{'stack'};
  7         14  
  7         21  
178 7         10 @{*{$location}} = ();
  7         9  
  7         23  
179             }
180              
181             sub DESTROY
182             {
183 4     4   3620 my($location) = @_;
184              
185 4 50       15 if ($Alive)
186             {
187 4 100       5 if (${*{$location}}{'file'} ne '')
  4         6  
  4         21  
188             {
189 3         8 &dump($location);
190             }
191 4         13 &_unlink_outer_($location);
192 4         1109 &_unlink_inner_($location);
193 4         9 &_resurrect_($location, ${*{$location}}{'refs'});
  4         4  
  4         21  
194 4         6 delete $Data::Locations::{ ${*{$location}}{'name'} };
  4         5  
  4         12  
195 4         7 { local($^W) = 0; untie(*{$location}); }
  4         18  
  4         5  
  4         25  
196 4         6 undef ${*{$location}};
  4         4  
  4         12  
197 4         5 undef %{*{$location}};
  4         5  
  4         18  
198 4         6 undef @{*{$location}};
  4         5  
  4         43  
199             }
200             }
201              
202             sub END
203             {
204 19     19   4287 my($item,$location);
205              
206             ## Disable auto-dump during global destruction and dump all relevant
207             ## locations here while all their embedded sublocations still exist
208             ## (because global destruction destroys in random order!):
209              
210 19         52 $Alive = 0;
211              
212 19         314 foreach $item (keys %Data::Locations::)
213             {
214 904 100       10841 if ($item =~ /^LOCATION\d+$/)
215             {
216 80         95 $location = ${*{ $Data::Locations::{$item} }};
  80         94  
  80         391  
217 80 50       188 if (${*{$location}}{'file'} ne '')
  80         86  
  80         289  
218             {
219 0         0 &dump($location);
220             }
221 80         207 &_resurrect_($location, ${*{$location}}{'refs'});
  80         98  
  80         287  
222             }
223             }
224             }
225              
226             sub filename
227             {
228 35 50 33 35 0 849 &_usage_('$filename = $location->filename( [ $filename ] );')
      33        
229             if ((@_ < 1) || (@_ > 2) || !ref($_[0]));
230              
231 35         43 my($location) = shift;
232 35         37 my($file);
233              
234 35         37 $file = ${*{$location}}{'file'};
  35         38  
  35         82  
235 35 100       88 if (@_ > 0)
236             {
237 21         60 ${*{$location}}{'file'} = &_check_filename_('filename', $_[0]);
  21         24  
  21         58  
238             }
239 35         529 return $file;
240             }
241              
242             sub toplevel
243             {
244 9 50 33 9 0 85 &_usage_('$flag = $location->toplevel();')
245             if ((@_ != 1) || !ref($_[0]));
246              
247 9         10 return ! keys(%{${*{$_[0]}}{'outer'}});
  9         7  
  9         10  
  9         37  
248             }
249              
250             sub _self_contained_
251             {
252 99     99   176 my($outer,$inner) = @_;
253 99         103 my($list,$item);
254              
255 99 100       384 return 1 if ($outer == $inner);
256 96         109 $list = ${*{$outer}}{'outer'};
  96         101  
  96         321  
257 96         137 foreach $item (keys %{$list})
  96         492  
258             {
259 60 50       142 if (exists $Data::Locations::{$item})
260             {
261 60         65 $outer = ${*{ $Data::Locations::{$item} }};
  60         102  
  60         158  
262 60 100       180 return 1 if (&_self_contained_($outer,$inner));
263             }
264             }
265 79         276 return 0;
266             }
267              
268             sub PRINT ## Aliased to "print"
269             {
270 164 50 33 164   1576 &_usage_('$location->print(@items);')
271             if ((@_ < 1) || !ref($_[0]));
272              
273 164         225 my($outer) = shift;
274 164         187 my($inner);
275              
276             ITEM:
277 164         261 foreach $inner (@_)
278             {
279 237 100       461 if (ref($inner))
280             {
281 40 100       122 if (ref($inner) ne $Class)
282             {
283 1         6 &_alert_("print", ref($inner) . " reference ignored");
284 1         6 next ITEM;
285             }
286 39 100       119 if (&_self_contained_($outer,$inner))
287             {
288 3         9 &_error_("print", "infinite recursion loop attempted");
289             }
290             else
291             {
292 36         52 ${${*{$inner}}{'outer'}}{${*{$outer}}{'name'}} = 1;
  36         95  
  36         98  
  36         69  
  36         40  
  36         103  
293 36         72 ${${*{$outer}}{'inner'}}{${*{$inner}}{'name'}} = 1;
  36         44  
  36         144  
  36         48  
  36         34  
  36         95  
294             }
295             }
296 233         247 push(@{*{$outer}}, $inner);
  233         222  
  233         1025  
297             }
298             }
299              
300             sub PRINTF ## Aliased to "printf"
301             {
302 2 50 33 2   43 &_usage_('$location->printf($format, @items);')
303             if ((@_ < 2) || !ref($_[0]));
304              
305 2         5 my($location) = shift;
306 2         4 my($format) = shift;
307              
308 2         41 &print( $location, sprintf($format, @_) );
309             }
310              
311             sub println
312             {
313 48 50 33 48 0 432 &_usage_('$location->println(@items);')
314             if ((@_ < 1) || !ref($_[0]));
315              
316 48         70 my($location) = shift;
317              
318 48         130 &print( $location, @_, "\n" );
319              
320             ## We use a separate "\n" here (instead of concatenating it
321             ## with the last item) in case the last item is a reference!
322             }
323              
324             sub _read_item_
325             {
326 436     436   507 my($location) = @_;
327 436         406 my($stack,$first,$index,$which,$item);
328              
329 436 100       409 if (exists ${*{$location}}{'stack'})
  436         376  
  436         10537  
330             {
331 402         420 $stack = ${*{$location}}{'stack'};
  402         384  
  402         826  
332             }
333             else
334             {
335 34         41 $stack = [ [ 0, ${*{$location}}{'name'} ] ];
  34         34  
  34         127  
336 34         83 ${*{$location}}{'stack'} = $stack;
  34         35  
  34         83  
337             }
338              
339 436 100       595 if (@{$stack})
  436         848  
340             {
341 396         492 $first = ${$stack}[0];
  396         541  
342 396         415 $index = ${$first}[0];
  396         590  
343 396         393 $which = ${$first}[1];
  396         553  
344 396 100 66     2851 if ((exists $Data::Locations::{$which}) &&
  396         3518  
345 396         366 ($index < @{*{ $Data::Locations::{$which} }}))
346             {
347 319         313 $item = ${*{ $Data::Locations::{$which} }}[$index];
  319         362  
  319         841  
348 319         450 ${$first}[0]++;
  319         500  
349 319 100       552 if (defined $item)
350             {
351 315 100       469 if (ref($item))
352             {
353 44 50       101 if (ref($item) eq $Class)
354             {
355 44         49 unshift(@{$stack}, [ 0, ${*{$item}}{'name'} ]);
  44         61  
  44         42  
  44         174  
356             }
357 44         677 return &_read_item_($location);
358             }
359 271         993 else { return $item; }
360             }
361 4         13 else { return ""; }
362             }
363             else
364             {
365 77         97 shift(@{$stack});
  77         715  
366 77         255 return &_read_item_($location);
367             }
368             }
369 40         204 else { return undef; }
370             }
371              
372             sub _read_list_
373             {
374 27     27   668 my($location) = @_;
375 27         33 my(@result);
376             my($item);
377              
378 27         51 while (defined ($item = &_read_item_($location)))
379             {
380 241         1321 push(@result, $item);
381             }
382 27         220 return( @result );
383             }
384              
385             sub READLINE ## Aliased to "read"
386             {
387 74 50 33 74   1406 &_usage_('[ $item | @list ] = $location->read();')
388             if ((@_ != 1) || !ref($_[0]));
389              
390 74         121 my($location) = @_;
391              
392 74 50       193 if (defined wantarray)
393             {
394 74 100       139 if (wantarray)
395             {
396 27         60 return( &_read_list_($location) );
397             }
398             else
399             {
400 47         88 return &_read_item_($location);
401             }
402             }
403             }
404              
405             sub reset
406             {
407 23 50 33 23 0 333 &_usage_('$location->reset();')
408             if ((@_ != 1) || !ref($_[0]));
409              
410 23         26 delete ${*{$_[0]}}{'stack'};
  23         33  
  23         128  
411             }
412              
413             sub _traverse_recursive_
414             {
415 21     21   24 my($location,$callback) = @_;
416 21         23 my($item);
417              
418 21         29 foreach $item (@{*{$location}})
  21         26  
  21         53  
419             {
420 79 100       353 if (ref($item))
421             {
422 18 50       37 if (ref($item) eq $Class)
423             {
424 18         48 &_traverse_recursive_($item,$callback);
425             }
426             }
427             else
428             {
429 61         60 &{$callback}($item);
  61         216  
430             }
431             }
432             }
433              
434             sub traverse
435             {
436 3 50 33 3 0 101 &_usage_('$location->traverse(\&callback_function);')
437             if ((@_ != 2) || !ref($_[0]));
438              
439 3         7 my($location,$callback) = @_;
440              
441 3 50       11 if (ref($callback) ne 'CODE')
442             {
443 0         0 &_error_("traverse", "not a code reference");
444             }
445 3         10 &_traverse_recursive_($location,$callback);
446             }
447              
448             sub _dump_recursive_
449             {
450 20     20   41 my($location,$filehandle) = @_;
451 20         19 my($item);
452              
453 20         23 foreach $item (@{*{$location}})
  20         23  
  20         50  
454             {
455 74 100       132 if (ref($item))
456             {
457 15 50       46 if (ref($item) eq $Class)
458             {
459 15         35 &_dump_recursive_($item,$filehandle);
460             }
461             }
462             else
463             {
464 59         208 print $filehandle $item;
465             }
466             }
467             }
468              
469             sub dump
470             {
471 6 50 33 6 0 516 &_usage_('$ok = $location->dump( [ $filename ] );')
      33        
472             if ((@_ < 1) || (@_ > 2) || !ref($_[0]));
473              
474 6         12 my($location) = shift;
475 6         9 my($file);
476              
477 6         17 local(*FILEHANDLE);
478              
479 6         10 $file = ${*{$location}}{'file'};
  6         7  
  6         25  
480 6 100       387 $file = shift if (@_ > 0);
481 6         20 $file = &_check_filename_('dump', $file);
482              
483 6 100       34 if ($file =~ /^\s*$/)
484             {
485 1         3 &_alert_("dump", "filename missing or empty");
486 1         3 return 0;
487             }
488 5 100       19 unless ($file =~ /^\s*[>\|+]/)
489             {
490 4         13 $file = '>' . $file;
491             }
492 5 50       519 unless (open(FILEHANDLE, $file))
493             {
494 0         0 &_alert_("dump", "can't open file '$file': \L$!\E");
495 0         0 return 0;
496             }
497 5         17 &_dump_recursive_($location,*FILEHANDLE);
498 5 50       249 unless (close(FILEHANDLE))
499             {
500 0         0 &_alert_("dump", "can't close file '$file': \L$!\E");
501 0         0 return 0;
502             }
503 5         20 return 1;
504             }
505              
506             sub tie
507             {
508 14 50 33 14 0 2217 &_usage_('$location->tie( [ "FH" | *FH | \*FH | *{FH} | \*{FH} | $fh ] );')
509             if ((@_ != 2) || !ref($_[0]));
510              
511 14         32 my($location,$filehandle) = @_;
512              
513 14         88 $filehandle =~ s/^\*//;
514 14         62 $filehandle = Symbol::qualify($filehandle, caller);
515 19     19   199 no strict "refs";
  19         43  
  19         1382  
516 14         134 tie(*{$filehandle}, $Class, $location);
  14         72  
517 19     19   187 use strict "refs";
  19         37  
  19         13595  
518             }
519              
520             1;
521              
522             __END__