File Coverage

blib/lib/Astro/Funtools/Parse.pm
Criterion Covered Total %
statement 127 129 98.4
branch 42 58 72.4
condition n/a
subroutine 13 13 100.0
pod 4 4 100.0
total 186 204 91.1


line stmt bran cond sub pod time code
1             package Astro::Funtools::Parse;
2              
3 1     1   42878 use strict;
  1         3  
  1         36  
4 1     1   6 use warnings;
  1         2  
  1         30  
5              
6 1     1   6 use Carp;
  1         6  
  1         66  
7 1     1   804 use IO::File;
  1         11693  
  1         247  
8              
9             require Exporter;
10              
11             our @ISA = qw(Exporter);
12              
13             # Items to export into callers namespace by default. Note: do not export
14             # names by default without a very good reason. Use EXPORT_OK instead.
15             # Do not simply export all your public functions/methods/constants.
16              
17             # This allows declaration use Astro::Parse::Funtools ':all';
18             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
19             # will save memory.
20             our %EXPORT_TAGS = ( 'all' => [ qw(
21             parse_funcnts
22             parse_funcnts_file
23             parse_funhist
24             parse_funhist_file
25             ) ] );
26              
27             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
28              
29             our @EXPORT = qw(
30            
31             );
32              
33             our $VERSION = '0.08';
34              
35 1     1   961 use Data::LineBuffer;
  1         493  
  1         2781  
36              
37              
38             # Preloaded methods go here.
39              
40             sub parse_funhist_file
41             {
42 1     1 1 5872 my $file = shift;
43 1 50       9 my $fh = new IO::File $file
44             or croak( __PACKAGE__, "::parse_funhist_file: unable to open $file\n" );
45              
46 1         88 parse_funhist( $fh );
47             }
48              
49             sub parse_funhist
50             {
51 1     1 1 2 my $what = shift;
52              
53 1         2 my %results;
54              
55 1 50       7 my $src = new Data::LineBuffer $what
56             or croak( __PACKAGE__, "parse_funcnts: something wrong with argument\n");
57              
58 1         22 my $header = _parse_header( $src );
59              
60 1 50       4 if ( exists $header->{_TOP}{'min,max,bins'} )
61             {
62 1         4 my ( $min, $max, $bins ) = split( ' ', $header->{_TOP}{'min,max,bins'} );
63 1         3 delete $header->{_TOP}{'min,max,bins'};
64 1         1 @{$header->{_TOP}}{qw( min max bins) } = ( $min, $max, $bins );
  1         5  
65             }
66              
67             # grab the first thing; it's a table
68 1         4 my $table = _parse_table( $src );
69              
70 1         51 ( $header->{_TOP}, $table );
71             }
72              
73             sub parse_funcnts_file
74             {
75 14     14 1 213259 my $file = shift;
76 14 50       155 my $fh = new IO::File $file
77             or croak( __PACKAGE__, "::parse_funcnts_file: unable to open $file\n" );
78              
79 14         1479 parse_funcnts( $fh );
80             }
81              
82              
83             sub parse_funcnts
84             {
85 14     14 1 33 my $what = shift;
86              
87 14         26 my @results;
88              
89 14 50       123 my $src = new Data::LineBuffer $what
90             or croak( __PACKAGE__, "parse_funcnts: something wrong with argument\n");
91              
92 14         365 LINE: while ( 1 )
93             {
94 40         58 my %results;
95              
96 40         87 $results{hdr} = _parse_header( $src );
97              
98 40 100       57 last unless %{$results{hdr}};
  40         159  
99            
100             # grab the first thing; it's a table
101 26         68 my $ln = $src->pos;
102 26         114 my $table = _parse_table( $src );
103            
104             # is it a summed background-subtracted table?
105 26 100       46 if ( grep { 'upto' eq $_ } @{$table->{names}} )
  216         389  
  26         50  
106             {
107 10         26 $results{sum_bkgd_sub}{table} = $table;
108             # next thing is the real background subtracted table, then
109 10         25 $ln = $src->pos;
110 10         37 $results{bkgd_sub}{table} = _parse_table( $src );
111            
112             # but we'll check on that!
113 84         131 croak( __PACKAGE__,
114             "::parse_funcnts: line $ln: expected a background-subtracted ",
115             "table but didn't find one!\n" )
116 10         26 unless grep { 'reg' eq $_ }
117 10 50       18 @{$results{bkgd_sub}{table}->{names}};
118             }
119            
120             # nope, must be a background-subtracted table
121             else
122             {
123 16         49 $results{bkgd_sub}{table} = $table;
124 132         245 croak( __PACKAGE__,
125             "::parse_funcnts: line $ln: expected a background-subtracted ",
126             "table but didn't find one!\n" )
127 16         33 unless grep { 'reg' eq $_ }
128 16 50       21 @{$table->{names}};
129             }
130            
131             # ok, now we're looking for regions, source and possibly background
132             # each region has a table after it.
133            
134 26         68 $results{source}{regions} = _parse_funcnts_regions( $src );
135              
136 26         57 $results{source}{table} = _parse_table( $src );
137            
138             # if there's a region left, it'll be the background
139            
140 26         59 my $regions = _parse_funcnts_regions( $src );
141 26 100       84 if ( %$regions )
142             {
143 24 100       29 if ( @{$regions->{regions}} )
  24         60  
144             {
145 20         49 $results{bkgd}{regions} = $regions;
146 20         37 $results{bkgd}{table} = _parse_table( $src );
147             }
148 24         67 _skip_past_formfeed( $src );
149             }
150 26         335 push @results, \%results;
151             }
152             return wantarray
153             ? @results
154 14 50       356 : $results[0];
155             }
156              
157             sub _skip_past_formfeed
158             {
159 24     24   29 my $src = shift;
160              
161 24         27 local $_;
162              
163 24         57 for( my $ln = $src->pos; defined ($_ = $src->get) ; $ln = $src->pos )
164             {
165 10 50       169 last if /^\f$/;
166             }
167             }
168              
169              
170             sub _parse_funcnts_regions
171             {
172 52     52   71 my $src = shift;
173              
174 52         51 local $_;
175              
176 52         54 my $title;
177             my @regions;
178              
179 52         129 while( defined ($_ = $src->get) )
180             {
181 100 100       1419 return {} if /^\f$/;
182 98 100       356 next unless /^#\s+(.*)$/;
183 46         174 /^#\s+((source|background)_region\(s\))/;
184 46 50       88 return unless $_;
185 46         103 $title = $1;
186 46         58 last;
187             }
188              
189 50         203 while( defined ($_ = $src->get) )
190             {
191 273 100       3835 last unless /^#\s+(.*)$/;
192 227         1224 push @regions, $1;
193             }
194              
195 50         294 { title => $title, regions => \@regions };
196             }
197              
198              
199              
200             sub _parse_header
201             {
202 41     41   51 my $src = shift;
203              
204 41         44 my %hdr;
205            
206 41         40 local $_;
207              
208 41         58 my $key = '_TOP';
209 41         115 for( my $ln = $src->pos; defined ($_ = $src->get) ; $ln = $src->pos )
210             {
211 302 100       5542 last unless /^\#/;
212 275 100       608 if ( /:/ )
213             {
214 197 50       376 croak( __PACKAGE__,
215             "::_parse_header: line $ln: missing key in header\n" )
216             unless defined $key;
217            
218 197         988 my ( $subkey, $val ) = /^\#\s+(.*):\s+(.*)/;
219 197         335 $subkey =~ s/\s+$//;
220 197         280 $subkey =~ s/\s/_/g;
221 197         241 $val =~ s/\s+$//;
222 197         919 $hdr{$key}{$subkey} = $val;
223             }
224            
225             else
226             {
227 78         284 ( $key ) = /^\#\s+(.*)/;
228 78         304 $key =~ s/\s+$//;
229             }
230             }
231            
232 41         365 \%hdr;
233             }
234              
235             sub _parse_table
236             {
237 83     83   97 my $src = shift;
238              
239 83         81 my @records;
240             my @comments;
241 0         0 my @names;
242              
243 83         101 local $_;
244              
245 83         81 my $ln;
246              
247             # search for start of table. look for a leading `-'
248             # ignore empty lines. anything with a leading `#' is a comment.
249             # anything else is the list of column names.
250 83         197 for( $ln = $src->pos ; defined ( $_ = $src->get ) ; $ln = $src->pos )
251             {
252 258 100       3993 next if /^\s*$/;
253              
254 248 100       627 last if /^-+/;
255              
256 165 100       397 if ( /^\#(.*)$/ )
257             {
258 82         352 push @comments, $1;
259             }
260              
261             else
262             {
263 83 50       183 croak( __PACKAGE__,
264             "::_parse_table: line $ln: more than one set of column headers?\n" )
265             if @names;
266              
267 83         146 chomp;
268 83         549 @names = split;
269             }
270             }
271              
272 83 50       177 croak( __PACKAGE__, "::_parse_table: line $ln: no table here!\n" )
273             unless defined $_;
274              
275 83         285 my @widths = map { length($_) } split(' ', $_ );
  476         720  
276 83 50       252 croak( __PACKAGE__,
277             "::_parse_table: line $ln: inconsistent number of column names and separators" )
278             if @names != @widths;
279              
280              
281             # work around extra blank line between header and data
282 83         218 $_ = $src->get;
283 83 50       1230 $src->unget( $_ )
284             unless /^\s*$/;
285              
286 83         603 for ( my $ln = $src->pos; defined ($_ = $src->get) ;$ln = $src->pos )
287             {
288 530 100       8647 last if /^\s*$/;
289              
290 448         544 chomp;
291 448         1983 my @data = split;
292 448 50       1117 unless ( @data == @names )
293             {
294 0         0 croak( __PACKAGE__,
295             "::_parse_table: line $ln: number of columns and number of data elements differ" )
296             }
297 448         502 my %data;
298 448         2508 @data{@names} = @data;
299 448         2549 push @records, \%data;
300             }
301              
302 83         505 return { comments => \@comments,
303             names => \@names,
304             widths => \@widths,
305             records => \@records };
306             }
307              
308              
309             1;
310             __END__