File Coverage

blib/lib/Text/NASA_Ames.pm
Criterion Covered Total %
statement 125 144 86.8
branch 25 38 65.7
condition 2 9 22.2
subroutine 20 21 95.2
pod 3 3 100.0
total 175 215 81.4


line stmt bran cond sub pod time code
1             package Pure::Text::NASA_Ames;
2 10     10   5097 use base qw(Class::Accessor);
  10         24  
  10         10756  
3              
4             Pure::Text::NASA_Ames->mk_accessors(qw(aMiss aName aScal date dX x
5             ffi iVol lenA lenX mName nAuxC nAuxV
6             nCom nIV nLHead nNComL nSComL nV nVol
7             nVPM nX nXDef oName org rDate sCom
8             sName vMiss vName vScal xName IO_File
9             fileName currentLine dataBuffer));
10            
11             package Text::NASA_Ames;
12              
13 10     10   47868 use Carp ();
  10         22  
  10         168  
14 10     10   14061 use IO::File ();
  10         176123  
  10         1785  
15              
16 10     10   313 use 5.00600;
  10         36  
  10         380  
17 10     10   57 use strict;
  10         24  
  10         358  
18              
19 10     10   57 use base qw(Pure::Text::NASA_Ames);
  10         20  
  10         9143  
20 10     10   9062 use Text::NASA_Ames::DataEntry;
  10         41  
  10         97  
21              
22             our $VERSION = 0.03;
23              
24             sub _carp {
25 0     0   0 my $self = shift;
26 0         0 my $fileMsg = '';
27 0 0 0     0 $fileMsg = " in ".$self->fileName. " line ".$self->IO_File->input_line_number if ($self->IO_File && $self->IO_File->input_line_number);
28 0         0 return Carp::carp(@_, $fileMsg);
29             }
30              
31             =head1 NAME
32              
33             Text::NASA_Ames - Reading data from NASA Ames files
34              
35             =head1 SYNOPSIS
36              
37             my $nasaAmes = new Text::NASA_Ames('this_file');
38             # or
39             $nasaAmes = new Text::NASA_Ames(new IO::File "this_file", "r");
40             print STDERR "3 independent variables";
41             $nasaAmes->nAuxV; # number of (real) auxiliary variables not containing
42             # a independent variable
43             $nasaAmes->nV; # number of independent variables
44              
45              
46             # scanning through the file:
47             while (my $entry = $nasaAmes->nextDataEntry) {
48             my @X = @{ $dataEntry->X };
49             my @aux = @{ $dataEntry->A };
50             my @V = @{ $dataEntry->V };
51             }
52              
53             =head1 DESCRIPTION
54              
55             This is the factory class for reading on of the Text::NASA_Ames formats
56             (currently 9). The function-names are related to the format specification
57             as in L by
58             Gaines and Hipkinds.
59              
60             =head1 PUBLIC METHODS
61              
62              
63              
64             =head2 METHODS FROM SPECIFICATIONS
65              
66             All lists are returned as references to lists.
67              
68             The names of the public methods are identical to the format specifications. In some formats, auxiliary variables are misused to store independent variables. In those cases, I return the real number of auxiliary variables with nAuxV and not nAuxV as sum of auxiliray variables plus stored independent variables as written in the file.
69              
70              
71             =over 4
72              
73             =item a
74              
75             not implemented, see L
76              
77             =item aMiss
78              
79             list of missing values of the auxiliary variables
80              
81             =item aName
82              
83             list of names of the auxiliary variables
84              
85             =item aScal
86              
87             list of scaling factors of the auxiliary variables
88              
89             =item date
90              
91             UT date marking the beginning of the data: format 'YYYY MM DD'
92              
93             =item dX
94              
95             list of interval between the indep. variables (0 means no linear interval)
96              
97             =item ffi
98              
99             File Format Index of the parsed file
100              
101             =item iVol
102              
103             total number of volumes belonging to complete dataset (see nVol)
104             (starting at 1)
105              
106             =item lenA
107              
108             list of character length of the auxiliary string variables
109              
110             =item lenX
111              
112             list of character length of the independent string variables
113              
114             =item mName
115              
116             mission name
117              
118             =item nAuxC
119              
120             number of auxiliary string variables
121              
122             =item nAuxV
123              
124             number of total auxiliary variables (including string variables). The numeric
125             auxiliary variables come allways in front of the string variables.
126              
127             =item nCom
128              
129             list of normal comment strings
130              
131             =item nIV
132              
133             number of independent variables (== first number of ffi)
134              
135             =item nLHead
136              
137             number of header lines
138              
139             =item nNComL
140              
141             number of normal comment lines
142              
143             =item nSComL
144              
145             number of special comment lines
146              
147             =item nV
148              
149             number of primary variables
150              
151             =item nVol
152              
153             total number of volumes required to store complete dataset
154              
155             =item nVPM
156              
157             number of independent variable between variable marks
158              
159             =item nX
160              
161             list of numbers of values of the primary variables
162              
163             =item nXDef
164              
165             list of numbers of values of the independent variable defined in the header
166              
167             =item oName
168              
169             originator name
170              
171             =item org
172              
173             originators organization
174              
175             =item rDate
176              
177             date of last revision (format as data)
178              
179             =item sCom
180              
181             list of special comment lines
182              
183             =item sName
184              
185             source name
186              
187             =item v
188              
189             not implemented, see L
190              
191             =item vMiss
192              
193             list of missing values of the variables
194              
195             =item vName
196              
197             list of variable names
198              
199             =item vScal
200              
201             list of scaling factor of variables
202              
203             =item x
204              
205             might be used for temporary variables, for iterator-access,
206             see L
207              
208             =item xName
209              
210             list of variable names
211              
212             =back
213              
214             =head2 ADDITIONAL METHODS
215              
216             =over 4
217              
218             =item new (filename || IO::File)
219              
220             parse the first line and distribute the file to the correct Text::NASA_Ames::FFI
221             object;
222              
223             =cut
224              
225             sub new {
226 26     26 1 229 my ($class, $file) = @_;
227 26   33     176 $class = ref $class || $class;
228 26         58 my $self = {};
229 26         64 bless $self, $class;
230              
231 26         201 $self->fileName("$file"); # scalar representation
232 26 50 33     562 if (ref ($file) && $file->isa("IO::File")) {
233 0         0 $self->IO_File($file);
234             } else {
235 26         247 $self->IO_File(new IO::File "$file", "r");
236             }
237 26 50       4387 unless (defined $self->IO_File) {
238 0         0 $self->_carp("couldn't initialize file $file: $!");
239 0         0 return undef;
240             }
241 26         558 $self->currentLine(1); # lines starting at 1
242 26         486 $self->dataBuffer([]);
243              
244 26         269 $self->_parseTopHeader;
245 26         1423 my $subclass = 'Text::NASA_Ames::FFI'.$self->ffi;
246 26         3588 eval "require $subclass";
247 26 50       124 if ($@) {
248 0         0 $self->_carp("cannot require $subclass");
249 0         0 return;
250             }
251 26         208 return $subclass->new($self);
252             }
253              
254              
255             sub _parseTopHeader {
256 26     26   43 my $self = shift;
257             # get the nlhead and format
258 26         88 my $line = $self->nextLine;
259 26         105 my ($nlhead, $ffi) = split ' ', $line;
260 26         160 $self->nLHead($nlhead);
261 26 50       330 if ($ffi =~ /(\d{4})/) {
262 26         74 $ffi = $1; # clean and untaint
263             }
264 26         128 $self->ffi($ffi);
265 26         382 $self->nIV( int ($ffi/1000) );
266 26         316 $self->oName($self->nextLine);
267 26         287 $self->org($self->nextLine);
268 26         256 $self->sName($self->nextLine);
269 26         424 $self->mName($self->nextLine);
270 26         259 my @vol = split ' ', $self->nextLine;
271 26         150 $self->iVol($vol[0]);
272 26         307 $self->nVol($vol[1]);
273 26         246 my @date = split ' ', $self->nextLine;
274 26         222 $self->date("$date[0] $date[1] $date[2]");
275 26         378 $self->rDate("$date[3] $date[4] $date[5]");
276 26 50       393 unless ($self->currentLine != 7) {
277 0         0 $self->_carp("problems reading top header, expected 7 lines, got ".
278             $self->currentLine);
279             }
280             }
281              
282             sub _parseTailHeader {
283 26     26   74 my $self = shift;
284 26         61 $self->nSComL($self->nextLine);
285 26         250 my @scom;
286 26         121 for (my $i = 0; $i < $self->nSComL; $i++) {
287 242         2552 push @scom, $self->nextLine;
288             }
289 26         1314 $self->sCom(\@scom);
290 26         270 $self->nNComL($self->nextLine);
291 26         245 my @com;
292 26         97 for (my $i = 0; $i < $self->nNComL; $i++) {
293 280         2623 push @com, $self->nextLine;
294             }
295 26         369 $self->nCom(\@com);
296 26 50       311 unless ($self->currentLine != 7) {
297 0         0 $self->_carp("problems reading header, expected ". $self->nLHeader() .
298             " lines, got ". $self->currentLine);
299             }
300             }
301              
302             # parse a list from a line expecting several entries
303             sub _parseList {
304 150     150   1584 my ($self, $type, $expected) = @_;
305 150 100       417 if ($expected != 0) {
306 134         298 my @list = split ' ', $self->nextLine;
307 134         1120 while (@list < $expected) {
308 0         0 push @list, split ' ', $self->nextLine;
309             }
310 134 50       302 if (@list > $expected) {
311 0         0 $self->_carp("got ".scalar @list .
312             " $type values, expected $expected\n");
313 0         0 return;
314             }
315 134         239 @list = map { $self->_trim($_) } @list;
  246         468  
316 134         1980 $self->$type(\@list);
317             }
318             }
319              
320             # parse several lines and join as list
321             sub _parseLines {
322 74     74   686 my ($self, $type, $lines) = @_;
323 74         87 my @values;
324 74         245 for (my $i = 0; $i < $lines; $i++) {
325 132         265 push @values, $self->nextLine;
326             }
327 74         128 @values = map {$self->_trim($_) } @values;
  132         297  
328 74         396 $self->$type(\@values);
329             }
330              
331             sub _parseVDeclaration {
332 26     26   50 my $self = shift;
333 26         80 $self->nV($self->nextLine);
334 26         321 foreach my $type (qw(vScal vMiss)) {
335 52         384 $self->_parseList($type, $self->nV);
336             }
337 26         342 $self->_parseLines('vName', $self->nV);
338             }
339              
340             sub _parseAuxDeclaration {
341 20     20   39 my $self = shift;
342 20         47 $self->nAuxV($self->nextLine);
343 20         196 foreach my $type (qw(aScal aMiss)) {
344 40         275 $self->_parseList($type, $self->nAuxV);
345             }
346 20         189 $self->_parseLines('aName', $self->nAuxV);
347             }
348              
349             =item currentLine
350              
351             the current line in the file, starting at 1
352              
353             =item nextLine
354              
355             get the next chomped/trimmed line from the file and set the currentLine counter
356              
357             =cut
358              
359             sub nextLine {
360 1394     1394 1 1783 my $self = shift;
361 1394         3299 my $fh = $self->IO_File;
362 1394         14632 my $line = <$fh>;
363 1394 100       4836 if (defined $line) {
364 1381         1624 chomp $line;
365 1381         3493 $self->currentLine($self->currentLine()+1);
366             }
367 1394         20986 return $self->_trim($line);
368             }
369              
370             =item dataBuffer
371              
372             set/get the complete dataBuffer. Don't use this method manually without
373             knowing what you're doing. Think about using L.
374              
375             =item nextDataEntry
376              
377             fetch the next L from the dataBuffer, which will
378             be filled automatically. The data will not be set to memory.
379              
380             =cut
381              
382             sub nextDataEntry {
383 822     822 1 5599 my $self = shift;
384 822         1783 my $buffer = $self->dataBuffer;
385              
386 822 100       6233 unless (@$buffer) {
387 131 50       545 if ($self->can('_refillBuffer')) {
388 131         349 $self->_refillBuffer;
389             } else {
390 0         0 $self->_carp("_refillBuffer not set for ".ref($self));
391             }
392             }
393 822         1834 return shift @$buffer;
394             }
395              
396             # takes to listRefs, sets the values in the second list to undef
397             # if they are equal to the same entry in the first list
398             sub _undefMissingVals {
399 869     869   1117 my ($self, $miss, $vals) = @_;
400 869 50       1765 if (@$miss != @$vals) {
401 0         0 $self->_carp("missing values ref (".scalar @$miss .
402             ") and values ref (".scalar @$vals .
403             ") should be of the same size");
404 0         0 return;
405             }
406 869         3604 for (my $i = 0; $i < @$miss; $i++) {
407 1165 50       2205 if (defined $vals->[$i]) {
408 1165 100       4184 if ($miss->[$i] eq $vals->[$i]) {
409 32         104 $vals->[$i] = undef;
410             }
411             }
412             }
413             }
414              
415             sub _scaleVals {
416 869     869   1087 my ($self, $scale, $vals) = @_;
417 869 50       1796 if (@$scale != @$vals) {
418 0         0 $self->_carp("scaling values ref and values ref should be of the same size");
419 0         0 return;
420             }
421 869         2462 for (my $i = 0; $i < @$scale; $i++) {
422 1165 100       2285 if (defined $vals->[$i]) {
423 1133 100       5928 $vals->[$i] *= $scale->[$i]
424             if (defined $scale->[$i]); # not defined for string values
425             }
426             }
427             }
428              
429             sub _cleanAndScaleVals {
430 869     869   14376 my ($self, $miss, $scale, $vals) = @_;
431 869         1787 $self->_undefMissingVals($miss, $vals);
432 869         1618 $self->_scaleVals($scale, $vals);
433             }
434              
435             sub _trim {
436 1772     1772   2041 my $self = shift;
437 1772         2861 local $_ = shift;
438 1772 100       3324 return unless defined $_;
439 1759         3678 s/^\s+//;
440 1759         5964 s/\s+$//;
441 1759         8562 return $_;
442             }
443              
444             1;
445             __END__