File Coverage

blib/lib/Astro/FITS/Header.pm
Criterion Covered Total %
statement 445 508 87.6
branch 156 220 70.9
condition 82 120 68.3
subroutine 52 59 88.1
pod 24 25 96.0
total 759 932 81.4


line stmt bran cond sub pod time code
1             package Astro::FITS::Header;
2              
3             # ---------------------------------------------------------------------------
4              
5             =head1 NAME
6              
7             Astro::FITS::Header - Object Orientated interface to FITS HDUs
8              
9             =head1 SYNOPSIS
10              
11             $header = new Astro::FITS::Header( Cards => \@array );
12              
13             =head1 DESCRIPTION
14              
15             Stores information about a FITS header block in an object. Takes an hash
16             with an array reference as an argument. The array should contain a list
17             of FITS header cards as input.
18              
19             =cut
20              
21             # L O A D M O D U L E S --------------------------------------------------
22              
23 10     10   114416 use strict;
  10         41  
  10         377  
24 10     10   68 use vars qw/ $VERSION /;
  10         45  
  10         555  
25 10     10   55 use Carp;
  10         26  
  10         871  
26              
27 10     10   4051 use Astro::FITS::Header::Item;
  10         66  
  10         425  
28              
29             $VERSION = '3.09';
30              
31             # Operator overloads
32 10         44 use overload '""' => "stringify",
33 10     10   73 fallback => 1;
  10         24  
34              
35             # C O N S T R U C T O R ----------------------------------------------------
36              
37             =head1 METHODS
38              
39             =head2 Constructor
40              
41             =over 4
42              
43             =item B
44              
45             Create a new instance from an array of FITS header cards.
46              
47             $item = new Astro::FITS::Header( Cards => \@header );
48              
49             returns a reference to a Header object. If you pass in no cards,
50             you get the (required) first SIMPLE card for free.
51              
52              
53             =cut
54              
55             sub new {
56 41     41 1 5502 my $proto = shift;
57 41   66     643 my $class = ref($proto) || $proto;
58              
59             # bless the header block into the class
60 41         203 my $block = bless { HEADER => [],
61             LOOKUP => {},
62             LASTKEY => undef,
63             TieRetRef => 0,
64             SUBHDRS => [],
65             }, $class;
66              
67             # Configure the object, even with no arguments since configure
68             # still puts the minimum SIMPLE card in.
69 41         131 $block->configure( @_ );
70              
71 41         151 return $block;
72              
73             }
74              
75             # I T E M ------------------------------------------------------------------
76              
77             =back
78              
79             =head2 Accessor Methods
80              
81             =over 4
82              
83             =item B
84              
85             Indicates whether the tied object should return multiple values
86             as a single string joined by newline characters (false) or
87             it should return a reference to an array containing all the values.
88              
89             Only affects the tied interface.
90              
91             tie %keywords, "Astro::FITS::Header", $header, tiereturnsref => 1;
92             $ref = $keywords{COMMENT};
93              
94             Defaults to returning a single string in all cases (for backwards
95             compatibility)
96              
97             =cut
98              
99             sub tiereturnsref {
100 356     356 1 3129 my $self = shift;
101 356 100       732 if (@_) {
102 4         9 $self->{TieRetRef} = shift;
103             }
104 356         938 return $self->{TieRetRef};
105             }
106              
107             =item B
108              
109             Set or return the subheaders for a Header object. Arguments must be
110             given as C objects.
111              
112             $header->subhdrs(@hdrs);
113             @hdrs = $header->subhdrs;
114              
115             This method should be used when you have additional header components
116             that should be associated with the primary header but they are not
117             associated with a particular name, just an ordering.
118              
119             FITS headers that are associated with a name can be stored directly
120             in the header using an C of type 'HEADER'.
121              
122             =cut
123              
124             sub subhdrs {
125 22     22 1 53 my $self = shift;
126              
127 22 100       61 if (@_) {
128             # verify the class
129 2         14 my $i;
130 2         38 for my $h (@_) {
131 4 50       16 croak "Argument $i supplied to subhdrs method is not a Astro::FITS::Header object\n"
132             unless UNIVERSAL::isa( $h, "Astro::FITS::Header" );
133 4         8 $i++;
134             }
135              
136             # store them
137 2         19 @{$self->{SUBHDRS}} = @_;
  2         6  
138             }
139 22 100       48 if (wantarray()) {
140 1         1 return @{$self->{SUBHDRS}};
  1         4  
141             } else {
142 21         118 return $self->{SUBHDRS};
143             }
144             }
145              
146             =item B
147              
148             Returns a FITS::Header:Item object referenced by index, C if it
149             does not exist.
150              
151             $item = $header->item($index);
152              
153             =cut
154              
155             sub item {
156 539     539 1 323940 my ( $self, $index ) = @_;
157              
158 539 50       1371 return undef unless defined $index;
159 539 50       777 return undef unless exists ${$self->{HEADER}}[$index];
  539         1385  
160              
161             # grab and return the Header::Item at $index
162 539         789 return ${$self->{HEADER}}[$index];
  539         1297  
163             }
164              
165              
166             =item B
167              
168             Returns a Starlink::AST FrameSet object representing the WCS of the
169             FITS Header.
170              
171             $ast = $header->get_wcs();
172              
173             =cut
174              
175             sub get_wcs {
176 0     0 1 0 my $self = shift;
177              
178 0         0 require Starlink::AST;
179 0         0 my $fchan = Starlink::AST::FitsChan->new();
180 0         0 for my $i ( $self->cards() ) {
181 0         0 $fchan->PutFits( $i, 0);
182             }
183 0         0 $fchan->Clear( "Card" );
184 0         0 return $fchan->Read();
185              
186             }
187              
188              
189             # K E Y W O R D ------------------------------------------------------------
190              
191             =item B
192              
193             Returns keyword referenced by index, C if it does not exist.
194              
195             $keyword = $header->keyword($index);
196              
197             =cut
198              
199             sub keyword {
200 249     249 1 94153 my ( $self, $index ) = @_;
201              
202 249 50       568 return undef unless defined $index;
203 249 100       350 return undef unless exists ${$self->{HEADER}}[$index];
  249         696  
204              
205             # grab and return the keyword at $index
206 247         388 return ${$self->{HEADER}}[$index]->keyword();
  247         658  
207             }
208              
209             # I T E M B Y N A M E -------------------------------------------------
210              
211             =item B
212              
213             Returns an array of Header::Items for the requested keyword if called
214             in list context, or the first matching Header::Item if called in scalar
215             context. Returns C if the keyword does not exist. The keyword
216             may be a regular expression created with the C operator.
217              
218             @items = $header->itembyname($keyword);
219             $item = $header->itembyname($keyword);
220              
221              
222              
223             =cut
224              
225             sub itembyname {
226 60     60 1 867 my ( $self, $keyword ) = @_;
227              
228 60         155 my @items = @{$self->{HEADER}}[$self->index($keyword)];
  60         164  
229              
230 60 100       238 return wantarray ? @items : @items ? $items[0] : undef;
    100          
231              
232             }
233              
234             # I T E M B Y T Y P E -------------------------------------------------
235              
236             =item B
237              
238             Returns an array of Header::Items for the requested type if called in
239             list context, or the first matching Header::Item if called in scalar
240             context. See C for a list of allowed types.
241              
242             @items = $header->itembytype( "COMMENT" );
243             @items = $header->itembytype( "HEADER" );
244             $item = $header->itembytype( "INT" );
245              
246             =cut
247              
248             sub itembytype {
249 1     1 1 678 my ( $self, $type ) = @_;
250              
251 1 50       15 return () unless defined $type;
252              
253 1         7 $type = uc($type);
254              
255             # No optimised lookup so brute force it
256 1         2 my @items = grep { $_->type eq $type } @{ $self->{HEADER} };
  123         222  
  1         4  
257              
258 1 0       10 return wantarray ? @items : @items ? $items[0] : undef;
    50          
259              
260             }
261              
262             # I N D E X --------------------------------------------------------------
263              
264             =item B
265              
266             Returns an array of indices for the requested keyword if called in
267             list context, or an empty array if it does not exist. The keyword may
268             be a regular expression created with the C operator.
269              
270             @index = $header->index($keyword);
271              
272             If called in scalar context it returns the first item in the array, or
273             C if the keyword does not exist.
274              
275             $index = $header->index($keyword);
276              
277             =cut
278              
279             sub index {
280 385     385 1 1294 my ( $self, $keyword ) = @_;
281              
282             # grab the index array from lookup table
283 385         597 my @index;
284              
285 385 100       782 if ( 'Regexp' eq ref $keyword ) {
286 13         27 push @index, @{$self->{LOOKUP}{$_}}
287 2         6 foreach grep { /$keyword/ &&
288 233 100       661 defined $self->{LOOKUP}{$_} } keys %{$self->{LOOKUP}};
  2         44  
289 2         45 @index = sort @index;
290             } else {
291 360         506 @index = @{${$self->{LOOKUP}}{$keyword}}
  360         874  
292 383         1184 if ( exists ${$self->{LOOKUP}}{$keyword} &&
293 383 100 66     538 defined ${$self->{LOOKUP}}{$keyword} );
  360         1175  
294             }
295              
296             # return the values array
297 385 50       1179 return wantarray ? @index : @index ? $index[0] : undef;
    100          
298              
299             }
300              
301             # V A L U E ---------------------------------------------------------------
302              
303             =item B
304              
305             Returns an array of values for the requested keyword if called in list
306             context, or an empty array if it does not exist. The keyword may be
307             a regular expression created with the C operator.
308              
309             @value = $header->value($keyword);
310              
311             If called in scalar context it returns the first item in the array, or
312             C if the keyword does not exist.
313              
314             =cut
315              
316             sub value {
317 295     295 1 2069 my ( $self, $keyword ) = @_;
318              
319             # resolve the values from the index array from lookup table
320 295         673 my @values = map { ${$self->{HEADER}}[$_]->value() } $self->index($keyword);
  302         439  
  302         906  
321              
322             # loop over the indices and grab the values
323 295 50       929 return wantarray ? @values : @values ? $values[0] : undef;
    100          
324              
325             }
326              
327             # C O M M E N T -------------------------------------------------------------
328              
329             =item B
330              
331             Returns an array of comments for the requested keyword if called
332             in list context, or an empty array if it does not exist. The keyword
333             may be a regular expression created with the C operator.
334              
335             @comment = $header->comment($keyword);
336              
337             If called in scalar context it returns the first item in the array, or
338             C if the keyword does not exist.
339              
340             $comment = $header->comment($keyword);
341              
342             =cut
343              
344             sub comment {
345 14     14 1 1526 my ( $self, $keyword ) = @_;
346              
347             # resolve the comments from the index array from lookup table
348             my @comments =
349 14         27 map { ${$self->{HEADER}}[$_]->comment() } $self->index($keyword);
  26         35  
  26         62  
350              
351             # loop over the indices and grab the comments
352 14 0       46 return wantarray ? @comments : @comments ? $comments[0] : undef;
    50          
353             }
354              
355             # I N S E R T -------------------------------------------------------------
356              
357             =item B
358              
359             Inserts a FITS header card object at position $index
360              
361             $header->insert($index, $item);
362              
363             the object $item is not copied, multiple inserts of the same object mean
364             that future modifications to the one instance of the inserted object will
365             modify all inserted copies.
366              
367             The insert position can be negative.
368              
369             =cut
370              
371             sub insert{
372 22     22 1 4192 my ($self, $index, $item) = @_;
373              
374             # splice the new FITS header card into the array
375             # Splice automatically triggers a lookup table rebuild
376 22         66 $self->splice($index, 0, $item);
377              
378 22         41 return;
379             }
380              
381              
382             # R E P L A C E -------------------------------------------------------------
383              
384             =item B
385              
386             Replace FITS header card at index $index with card $item
387              
388             $card = $header->replace($index, $item);
389              
390             returns the replaced card.
391              
392             =cut
393              
394             sub replace{
395 1     1 1 4 my ($self, $index, $item) = @_;
396             # remove the specified item and replace with $item
397             # Splice triggers a rebuild so we do not have to
398 1         7 return $self->splice( $index, 1, $item);
399             }
400              
401             # R E M O V E -------------------------------------------------------------
402              
403             =item B
404              
405             Removes a FITS header card object at position $index
406              
407             $card = $header->remove($index);
408              
409             returns the removed card.
410              
411             =cut
412              
413             sub remove{
414 4     4 1 659 my ($self, $index) = @_;
415             # remove the FITS header card from the array
416             # Splice always triggers a lookup table rebuild so we don't have to
417 4         13 return $self->splice( $index, 1);
418             }
419              
420             # R E P L A C E B Y N A M E ---------------------------------------------
421              
422             =item B
423              
424             Replace FITS header cards with keyword $keyword with card $item
425              
426             $card = $header->replacebyname($keyword, $item);
427              
428             returns the replaced card. The keyword may be a regular expression
429             created with the C operator.
430              
431             =cut
432              
433             sub replacebyname{
434 1     1 1 655 my ($self, $keyword, $item) = @_;
435              
436             # grab the index array from lookup table
437 1         6 my @index = $self->index($keyword);
438              
439             # loop over the keywords
440             # We use a real splice rather than the class splice for efficiency
441             # in order to prevent an index rebuild for each index
442 1         10 my @cards = map { splice @{$self->{HEADER}}, $_, 1, $item;} @index;
  1         2  
  1         5  
443              
444             # force rebuild
445 1         8 $self->_rebuild_lookup;
446              
447             # return removed items
448 1 50       7 return wantarray ? @cards : $cards[scalar(@cards)-1];
449              
450             }
451              
452             # R E M O V E B Y N A M E -----------------------------------------------
453              
454             =item B
455              
456             Removes a FITS header card object by name
457              
458             @card = $header->removebyname($keyword);
459              
460             returns the removed cards. The keyword may be a regular expression
461             created with the C operator.
462              
463             =cut
464              
465             sub removebyname{
466 6     6 1 48 my ($self, $keyword) = @_;
467              
468             # grab the index array from lookup table
469 6         17 my @index = $self->index($keyword);
470              
471             # loop over the keywords
472             # We use a real splice rather than the class splice for efficiency
473             # in order to prevent an index rebuild for each index. The ugly code
474             # is needed in case we have multiple indices returned, which can
475             # happen if we have a regular expression passed in as a keyword.
476 6         25 my $i = -1;
477 6         25 my @cards = map { $i++; splice @{$self->{HEADER}}, ( $_ - $i ), 1; } sort @index;
  11         18  
  11         16  
  11         33  
478              
479             # force rebuild
480 6         26 $self->_rebuild_lookup;
481              
482             # return removed items
483 6 50       49 return wantarray ? @cards : $cards[scalar(@cards)-1];
484             }
485              
486             # S P L I C E --------------------------------------------------------------
487              
488             =item B
489              
490             Implements a standard splice operation for FITS headers
491              
492             @cards = $header->splice($offset [,$length [, @list]]);
493             $last_card = $header->splice($offset [,$length [, @list]]);
494              
495             Removes the FITS header cards from the header designated by $offset and
496             $length, and replaces them with @list (if specified) which must be an
497             array of FITS::Header::Item objects. Returns the cards removed. If offset
498             is negative, counts from the end of the FITS header.
499              
500             =cut
501              
502             sub splice {
503 29     29 1 46 my $self = shift;
504 29         57 my ($offset, $length, @list) = @_;
505              
506             # If the array is empty and we get a negative offset we
507             # must convert it to an offset of 0 to prevent a:
508             # Modification of non-creatable array value attempted, subscript -1
509             # fatal error
510             # This can occur with a tied hash and the %{$tieref} = %new
511             # construct
512 29 50       78 if (defined $offset) {
513 29 100 66     39 $offset = 0 if (@{$self->{HEADER}} == 0 && $offset < 0);
  29         92  
514             }
515              
516             # the removed cards
517 29         49 my @cards;
518              
519 29 100       64 if (@list) {
    50          
    0          
520             # all arguments supplied
521 24         38 my $n = 0;
522 24         40 for my $i (@list) {
523 24 50       87 croak "Argument $n to splice must be Astro::FITS::Header::Item objects"
524             unless UNIVERSAL::isa($i, "Astro::FITS::Header::Item");
525 24         47 $n++;
526             }
527 24         31 @cards = splice @{$self->{HEADER}}, $offset, $length, @list;
  24         77  
528              
529             } elsif (defined $length) {
530             # length and (presumably) offset
531 5         9 @cards = splice @{$self->{HEADER}}, $offset, $length;
  5         17  
532              
533             } elsif (defined $offset) {
534             # offset only
535 0         0 @cards = splice @{$self->{HEADER}}, $offset;
  0         0  
536             } else {
537             # none
538 0         0 @cards = splice @{$self->{HEADER}};
  0         0  
539             }
540              
541             # update the internal lookup table and return
542 29         87 $self->_rebuild_lookup();
543 29 100       94 return wantarray ? @cards : $cards[scalar(@cards)-1];
544             }
545              
546             # C A R D S --------------------------------------------------------------
547              
548             =item B
549              
550             Return the object contents as an array of FITS cards.
551              
552             @array = $header->cards;
553              
554             =cut
555              
556             sub cards {
557 7     7 1 377 my $self = shift;
558 7         24 return map { "$_" } @{$self->{HEADER}};
  160         342  
  7         25  
559             }
560              
561             =item B
562              
563             Returns the highest index in use in the FITS header.
564             To get the total number of header items, add 1.
565              
566             $number = $header->sizeof;
567              
568             =cut
569              
570             sub sizeof {
571 13     13 1 1570 my $self = shift;
572 13         23 return $#{$self->{HEADER}};
  13         82  
573             }
574              
575             # A L L I T E M S ---------------------------------------------------------
576              
577             =item B
578              
579             Returns the header as an array of FITS::Header:Item objects.
580              
581             @items = $header->allitems();
582              
583             =cut
584              
585             sub allitems {
586 19     19 1 36 my $self = shift;
587 19         28 return map { $_ } @{$self->{HEADER}};
  750         1049  
  19         46  
588             }
589              
590             # C O N F I G U R E -------------------------------------------------------
591              
592             =back
593              
594             =head2 General Methods
595              
596             =over 4
597              
598             =item B
599              
600             Configures the object, takes an array of FITS header cards,
601             an array of Astro::FITS::Header::Item objects or a simple hash as input.
602             If you feed in nothing at all, it uses a default array containing
603             just the SIMPLE card required at the top of all FITS files.
604              
605             $header->configure( Cards => \@array );
606             $header->configure( Items => \@array );
607             $header->configure( Hash => \%hash );
608              
609             Does nothing if the array is not supplied. If the hash scheme is used
610             and the hash contains the special key of SUBHEADERS pointing to an
611             array of hashes, these will be read as proper sub headers. All other
612             references in the hash will be ignored. Note that the default key
613             order will be retained in the object created via the hash.
614              
615             =cut
616              
617             sub configure {
618 42     42 1 80 my $self = shift;
619              
620             # grab the argument list
621 42         101 my %args = @_;
622              
623 42 100 66     183 if (exists $args{Cards} && defined $args{Cards}) {
    50 33        
    50 33        
    0 0        
624              
625             # First translate each incoming card into a Item object
626             # Any existing cards are removed
627 40         383 @{$self->{HEADER}} = map {
628 1284         2802 new Astro::FITS::Header::Item( Card => $_ );
629 40         57 } @{ $args{Cards} };
  40         86  
630              
631             # Now build the lookup table. There would be a slight efficiency
632             # gain to include this in a loop over the cards but prefer
633             # to reuse the method for this rather than repeating code
634 40         132 $self->_rebuild_lookup;
635              
636             } elsif (exists $args{Items} && defined $args{Items}) {
637             # We have an array of Astro::FITS::Header::Items
638 0         0 @{$self->{HEADER}} = @{ $args{Items} };
  0         0  
  0         0  
639 0         0 $self->_rebuild_lookup;
640             } elsif (exists $args{Hash} && defined $args{Hash} ) {
641             # we have a hash so convert to Item objects and store
642             # use a For loop instead of map since we want to
643             # skip some items
644             croak "Hash constructor requested but not given a hash reference"
645 2 50       6 unless ref($args{Hash}) eq 'HASH';
646 2         3 my @items;
647             my @subheaders;
648 2         3 for my $k (keys %{$args{Hash}}) {
  2         8  
649 2 50 33     9 if ($k eq 'SUBHEADERS'
    50 33        
650             && ref($args{Hash}->{$k}) eq 'ARRAY'
651             && ref($args{Hash}->{$k}->[0]) eq 'HASH') {
652             # special case
653 0         0 @subheaders = map { $self->new( Hash => $_ ) } @{$args{Hash}->{$k}};
  0         0  
  0         0  
654             } elsif (not ref($args{Hash}->{$k})) {
655             # if we have new lines in the value, we should duplicate the item
656             # so split on new lines
657 2         4 my $value = $args{Hash}->{$k};
658 2 50       4 $value = '' unless defined $value;
659 2         6 my @lines = split(/^/m,$value);
660 2         4 chomp(@lines); # remove the newlines
661              
662 2         4 push(@items, map { new Astro::FITS::Header::Item( Keyword => $k,
  2         5  
663             Value => $_ ) }
664             @lines);
665             }
666             }
667 2         3 @{$self->{HEADER}} = @items;
  2         5  
668 2         7 $self->_rebuild_lookup;
669 2 50       7 $self->subhdrs(@subheaders) if @subheaders;
670             } elsif ( !defined($self->{HEADER}) || !@{$self->{HEADER}} ) {
671 0         0 @{$self->{HEADER}} = (
  0         0  
672             new Astro::FITS::Header::Item( Card=> "SIMPLE = T"),
673             new Astro::FITS::Header::Item( Card=> "END", Type=>"END" )
674             );
675 0         0 $self->_rebuild_lookup;
676             }
677             }
678              
679             =item B
680              
681             Given the current header and a set of C objects,
682             return a merged FITS header (with the cards that have the same value
683             and comment across all headers) along with, for each input, header
684             objects containing all the header items that differ (including, by
685             default, keys that are not present in all headers). Only the primary
686             headers are merged, subheaders are ignored.
687              
688             ($clone) = $headerr->merge_primary();
689             ($same, @different) = $header->merge_primary( $fits1, $fits2, ...);
690             ($same, @different) = $header->merge_primary( \%options, $fits1, $fits2 );
691              
692             @different can be empty if all headers match (but see the
693             C option) but if any headers are different there
694             will always be the same number of headers in @different as supplied to
695             the function (including the reference header). A clone of the input header
696             (stripped of any subheaders) is returned if no comparison headers are
697             supplied.
698              
699             In scalar context, just returns the merged header.
700              
701             $merged = $header->merge_primary( @hdrs );
702              
703             The options hash is itself optional. It contains the following keys:
704              
705             merge_unique - if an item is identical across multiple headers and only
706             exists in those headers, propagate to the merged header rather
707             than storing it in the difference headers.
708              
709             force_return_diffs - return an empty difference object per input header
710             even if there are no diffs
711              
712             =cut
713              
714             sub merge_primary {
715 8     8 1 1772 my $self = shift;
716              
717             # optional options handling
718 8         25 my %opt = ( merge_unique => 0,
719             force_return_diffs => 0,
720             );
721 8 100       26 if (ref($_[0]) eq 'HASH') {
722 3         7 my $o = shift;
723 3         15 %opt = ( %opt, %$o );
724             }
725              
726             # everything else is fits headers
727             # If we do not get any additional headers we still process the full header
728             # rather than shortcircuiting the logic. This is so that we can strip
729             # HEADER items without having to write duplicate logic. Clearly not
730             # very efficient but we do not really expect people to use this method
731             # to clone a FITS header....
732 8         26 my @fits = @_;
733              
734             # Number of output diff arrays
735             # Include this object
736 8         19 my $nhdr = @fits + 1;
737              
738             # Go through all the items building up a hash indexed
739             # by KEYWORD pointing to an array of items with that keyword
740             # and an array of unique keywords in the original order they
741             # appeared first. COMMENT items are stored in the
742             # hash as complete cards.
743             # HEADER items are currently dropped on the floor.
744 8         13 my @order;
745             my %items;
746 8         14 my $hnum = 0;
747 8         19 for my $hdr ($self, @fits) {
748 17         37 for my $item ($hdr->allitems) {
749 373         506 my $key;
750 373         691 my $type = $item->type;
751 373 50 33     1245 if (!defined $type || $type eq 'BLANK') {
    100          
    50          
752             # blank line so skip it
753 0         0 next;
754             } elsif ($type eq 'COMMENT') {
755 30         63 $key = $item->card;
756             } elsif ($type eq 'HEADER') {
757 0         0 next;
758             } else {
759 343         1949 $key = $item->keyword;
760             }
761              
762 373 100       692 if (exists $items{$key}) {
763             # Store the item, but in a hash with key corresponding
764             # to the input header number
765 197         251 push( @{ $items{$key}}, { item => $item, hnum => $hnum } );
  197         605  
766             } else {
767 176         461 $items{$key} = [ { item => $item, hnum => $hnum } ];
768 176         359 push(@order, $key);
769             }
770             }
771 17         62 $hnum++;
772             }
773              
774             # create merged and difference arrays
775 8         17 my @merged;
776 8         23 my @difference = map { [] } (1..$nhdr);
  17         37  
777              
778             # Now loop over all of the unique keywords (taking care to
779             # spot comments)
780 8         17 for my $key (@order) {
781 176         243 my @items = @{$items{$key}};
  176         332  
782              
783             # compare each Item with the first. This will work even if we only have
784             # one Item in the array.
785             # Note that $match == 1 to start with because it always matches itself
786             # but we do not bother doing the with-itself comparison.
787 176         232 my $match = 1;
788 176         317 for my $i (@items[1..$#items]) {
789             # Ask the Items to compare using the equals() method
790 197 100       437 if ($items[0]->{item}->equals( $i->{item} )) {
791 184         377 $match++;
792             }
793             }
794              
795             # if we matched all the items and are merging unique OR if we
796             # matched all the items and that was all the available headers
797             # we store in the merged array. Else we store in the differences
798             # array
799 176 100 100     567 if ($match == @items && ($match == $nhdr || $opt{merge_unique})) {
      100        
800             # Matched all the headers or merging matching unique headers
801             # only need to store one
802 165         339 push(@merged, $items[0]->{item});
803              
804             } else {
805             # Not enough of the items matched. Store to the relevant difference
806             # arrays.
807 11         33 for my $i (@items) {
808 26         45 push(@{ $difference[$i->{hnum}] }, $i->{item});
  26         90  
809             }
810              
811             }
812              
813             }
814              
815             # and clear @difference in the special case where none have any headers
816 8 100       19 if (!$opt{force_return_diffs}) {
817 7 100       15 @difference = () unless grep { @$_ != 0 } @difference;
  15         45  
818             }
819              
820             # unshift @merged onto the front of @difference in preparation
821             # for returning it
822 8         20 unshift(@difference, \@merged );
823              
824             # convert back to FITS object, Construct using the Items directly
825             # - they will be copied without strinfication.
826 8         17 for my $d (@difference) {
827 21         41 $d = $self->new( Cards => $d );
828             }
829              
830             # remembering that the merged array is on the front
831 8 100       197 return (wantarray ? @difference : $difference[0]);
832             }
833              
834             =item B
835              
836             Method to return a blessed reference to the object so that we can store
837             ths object on disk using Data::Dumper module.
838              
839             =cut
840              
841             sub freeze {
842 0     0 1 0 my $self = shift;
843 0         0 return bless $self, 'Astro::FITS::Header';
844             }
845              
846             =item B
847              
848             Append or update a card.
849              
850             $header->append( $card );
851              
852             This method can take either an Astro::FITS::Header::Item object, an
853             Astro::FITS::Header object, or a reference to an array of
854             Astro::FITS::Header::Item objects.
855              
856             In all cases, if the given Astro::FITS::Header::Item keyword exists in
857             the header, then the value will be overwritten with the one passed to
858             the method. Otherwise, the card will be appended to the end of the
859             header.
860              
861             Nothing is returned.
862              
863             =cut
864              
865             sub append {
866 0     0 1 0 my $self = shift;
867 0         0 my $thing = shift;
868              
869 0         0 my @cards;
870 0 0       0 if ( UNIVERSAL::isa( $thing, "Astro::FITS::Header::Item" ) ) {
    0          
    0          
871 0         0 push @cards, $thing;
872             } elsif ( UNIVERSAL::isa( $thing, "Astro::FITS::Header" ) ) {
873 0         0 @cards = $thing->allitems;
874             } elsif ( ref( $thing ) eq 'ARRAY' ) {
875 0         0 @cards = @$thing;
876             }
877              
878 0         0 foreach my $card ( @cards ) {
879 0         0 my $item = $self->itembyname( $card->keyword );
880 0 0       0 if ( defined( $item ) ) {
881              
882             # Update the given card.
883 0         0 $self->replacebyname( $card->keyword, $card )
884              
885             } else {
886              
887             # Don't append a SIMPLE header as that can lead to disaster and
888             # strife and gnashing of teeth (and violates the FITS standard).
889 0 0       0 next if ( uc( $card->keyword ) eq 'SIMPLE' );
890              
891             # Retrieve the index of the END card, and insert this card
892             # before that one, but only if the END header actually exists.
893 0         0 my $index = $self->index( 'END' );
894 0 0       0 $index = ( defined( $index ) ? $index : -1 );
895 0         0 $self->insert( $index, $card );
896             }
897             }
898              
899 0         0 $self->_rebuild_lookup;
900             }
901              
902             # P R I V A T E M E T H O D S ------------------------------------------
903              
904             =back
905              
906             =head2 Operator Overloading
907              
908             These operators are overloaded:
909              
910             =over 4
911              
912             =item B<"">
913              
914             When the object is used in a string context the FITS header
915             block is returned as a single string.
916              
917             =cut
918              
919             sub stringify {
920 5     5 0 25 my $self = shift;
921 5         16 return join("\n", $self->cards )."\n";
922             }
923              
924             =back
925              
926             =head2 Private methods
927              
928             These methods are for internal use only.
929              
930             =over 4
931              
932             =item B<_rebuild_lookup>
933              
934             Private function used to rebuild the lookup table after modifying the
935             header block, its easier to do it this way than go through and add one
936             to the indices of all header cards following the modified card.
937              
938             =cut
939              
940             sub _rebuild_lookup {
941 78     78   131 my $self = shift;
942              
943             # rebuild the lookup table
944              
945             # empty the hash
946 78         566 $self->{LOOKUP} = { };
947              
948             # loop over the existing header array
949 78         149 for my $j (0 .. $#{$self->{HEADER}}) {
  78         242  
950              
951             # grab the keyword from each header item;
952 3538         4557 my $key = ${$self->{HEADER}}[$j]->keyword();
  3538         7341  
953              
954             # need to account to repeated keywords (e.g. COMMENT)
955 3538 100 66     4590 unless ( exists ${$self->{LOOKUP}}{$key} &&
  3538         7613  
956 84         236 defined ${$self->{LOOKUP}}{$key} ) {
957             # new keyword
958 3454         5315 ${$self->{LOOKUP}}{$key} = [ $j ];
  3454         7631  
959             } else {
960             # keyword exists, push the current index into the array
961 84         112 push( @{${$self->{LOOKUP}}{$key}}, $j );
  84         109  
  84         226  
962             }
963             }
964              
965             }
966              
967             # T I E D I N T E R F A C E -----------------------------------------------
968              
969             =back
970              
971             =head1 TIED INTERFACE
972              
973             The C object can also be tied to a hash:
974              
975             use Astro::FITS::Header;
976              
977             $header = new Astro::FITS::Header( Cards => \@array );
978             tie %hash, "Astro::FITS::Header", $header
979              
980             $value = $hash{$keyword};
981             $hash{$keyword} = $value;
982              
983             print "keyword $keyword is present" if exists $hash{$keyword};
984              
985             foreach my $key (keys %hash) {
986             print "$key = $hash{$key}\n";
987             }
988              
989             =head2 Basic hash translation
990              
991             Header value type is determined on-the-fly by parsing of the input values.
992             Anything that parses as a number or a logical is converted to that before
993             being put in a card (but see below).
994              
995             Per-card comment fields can be accessed using the tied interface by specifying
996             a key name of "key_COMMENT". This works because in general "_COMMENT" is too
997             long to be confused with a normal key name.
998              
999             $comment = $hdr{CRPIX1_COMMENT};
1000              
1001             will return the comment associated with CRPIX1 header item. The comment
1002             can be modified in the same way:
1003              
1004             $hdr{CRPIX1_COMMENT} = "An axis";
1005              
1006             You can also modify the comment by slash-delimiting it when setting the
1007             associated keyword:
1008              
1009             $hdr{CRPIX1} = "34 / Set this field manually";
1010              
1011             If you want an actual slash character in your string field you must escape
1012             it with a backslash. (If you're in double quotes you have to use a double
1013             backslash):
1014              
1015             $hdr{SLASHSTR} = 'foo\/bar / field contains "foo/bar"';
1016              
1017             Keywords are CaSE-inNSEnSiTIvE, unlike normal hash keywords. All
1018             keywords are translated to upper case internally, per the FITS standard.
1019              
1020             Aside from the SIMPLE and END keywords, which are automagically placed at
1021             the beginning and end of the header respectively, keywords are included
1022             in the header in the order received. This gives you a modicum of control
1023             over card order, but if you actually care what order they're in, you
1024             probably don't want the tied interface.
1025              
1026             =head2 Comment cards
1027              
1028             Comment cards are a special case because they have no normal value and
1029             their comment field is treated as the hash value. The keywords
1030             "COMMENT" and "HISTORY" are magic and refer to comment cards; nearly all other
1031             keywords create normal valued cards. (see "SIMPLE and END cards", below).
1032              
1033             =head2 Multi-card values
1034              
1035             Multiline string values are broken up, one card per line in the
1036             string. Extra-long string values are handled gracefully: they get
1037             split among multiple cards, with a backslash at the end of each card
1038             image. They're transparently reassembled when you access the data, so
1039             that there is a strong analogy between multiline string values and multiple
1040             cards.
1041              
1042             In general, appending to hash entries that look like strings does what
1043             you think it should. In particular, comment cards have a newline
1044             appended automatically on FETCH, so that
1045              
1046             $hash{HISTORY} .= "Added multi-line string support";
1047              
1048             adds a new HISTORY comment card, while
1049              
1050             $hash{TELESCOP} .= " dome B";
1051              
1052             only modifies an existing TELESCOP card.
1053              
1054             You can make multi-line values by feeding in newline-delimited
1055             strings, or by assigning from an array ref. If you ask for a tag that
1056             has a multiline value it's always expanded to a multiline string, even
1057             if you fed in an array ref to start with. That's by design: multiline
1058             string expansion often acts as though you are getting just the first
1059             value back out, because perl string-to-number conversion stops at the
1060             first newline. So:
1061              
1062             $hash{CDELT1} = [3,4,5];
1063             print $hash{CDELT1} + 99,"\n$hash{CDELT1}";
1064              
1065             prints "102\n3\n4\n5", and then
1066              
1067             $hash{CDELT1}++;
1068             print $hash{CDELT1};
1069              
1070             prints "4".
1071              
1072             In short, most of the time you get what you want. But you can always fall
1073             back on the non-tied interface by calling methods like so:
1074              
1075             ((tied $hash)->method())
1076              
1077             If you prefer to have multi-valued items automagically become array
1078             refs, then you can get that behavior using the C method:
1079              
1080             tie %keywords, "Astro::FITS::Header", $header, tiereturnsref => 1;
1081              
1082             When tiereturnsref is true, multi-valued items will be returned via a
1083             reference to an array (ties do not respect calling context). Note that
1084             if this is configured you will have to test each return value to see
1085             whether it is returning a real value or a reference to an array if you
1086             are not sure whether there will be more than one card with a duplicate
1087             name.
1088              
1089             =head2 Type forcing
1090              
1091             Because perl uses behind-the-scenes typing, there is an ambiguity
1092             between strings and numeric and/or logical values: sometimes you want
1093             to create a STRING card whose value could parse as a number or as a
1094             logical value, and perl kindly parses it into a number for you. To
1095             force string evaluation, feed in a trivial array ref:
1096              
1097             $hash{NUMSTR} = 123; # generates an INT card containing 123.
1098             $hash{NUMSTR} = "123"; # generates an INT card containing 123.
1099             $hash{NUMSTR} = ["123"]; # generates a STRING card containing "123".
1100             $hash{NUMSTR} = [123]; # generates a STRING card containing "123".
1101              
1102             $hash{ALPHA} = "T"; # generates a LOGICAL card containing T.
1103             $hash{ALPHA} = ["T"]; # generates a STRING card containing "T".
1104              
1105             Calls to keys() or each() will, by default, return the keywords in the order
1106             in which they appear in the header.
1107              
1108             =head2 Sub-headers
1109              
1110             When the key refers to a subheader entry (ie an item of type
1111             "HEADER"), a hash reference is returned. If a hash reference is
1112             stored in a value it is converted to a C object.
1113              
1114             If the special key "SUBHEADERS" is used, it will return the array of
1115             subheaders, (as stored using the C method) each of which will
1116             be tied to a hash. Subheaders can be stored using normal array operations.
1117              
1118             =head2 SIMPLE and END cards
1119              
1120             No FITS interface would becomplete without special cases.
1121              
1122             When you assign to SIMPLE or END, the tied interface ensures that they
1123             are first or last, respectively, in the deck -- as the FITS standard
1124             requires. Other cards are inserted in between the first and last
1125             elements, in the order that you define them.
1126              
1127             The SIMPLE card is forced to FITS LOGICAL (boolean) type. The FITS
1128             standard forbids you from setting it to F, but you can if you want --
1129             we're not the FITS police.
1130              
1131             The END card is forced to a null type, so any value you assign to it
1132             will fall on the floor. If present in the deck, the END keyword
1133             always contains the value " ", which is both more-or-less invisible
1134             when printed and also true -- so you can test the return value to see
1135             if an END card is present.
1136              
1137             SIMPLE and END come pre-defined from the constructor. If for some
1138             nefarious reason you want to remove them you must explicitly do so
1139             with "delete" or the appropriate method call from the object
1140             interface.
1141              
1142             =cut
1143              
1144             # List of known comment-type fields
1145             %Astro::FITS::Header::COMMENT_FIELD = (
1146             "COMMENT"=>1,
1147             "HISTORY"=>1
1148             );
1149              
1150              
1151             # constructor
1152             sub TIEHASH {
1153 25     25   851 my ( $class, $obj, %options ) = @_;
1154 25         44 my $newobj = bless $obj, $class;
1155              
1156             # Process options
1157 25         58 for my $key (keys %options) {
1158 2         5 my $method = lc($key);
1159 2 50       14 if ($newobj->can($method)) {
1160 2         16 $newobj->$method( $options{$key});
1161             }
1162             }
1163              
1164 25         77 return $newobj;
1165             }
1166              
1167             # fetch key and value pair
1168             # MUST return undef if the key is missing else autovivification of
1169             # sub header will fail
1170              
1171             sub FETCH {
1172 180     180   17080 my ($self, $key) = @_;
1173              
1174 180         372 $key = uc($key);
1175              
1176             # if the key is called SUBHEADERS we should tie to an array
1177 180 100       458 if ($key eq 'SUBHEADERS') {
1178 2         4 my @dummy;
1179 2         9 tie @dummy, "Astro::FITS::HeaderCollection", scalar $self->subhdrs;
1180 2         11 return \@dummy;
1181             }
1182              
1183             # If the key has a _COMMENT suffix we are looking for a comment
1184 178         298 my $wantvalue = 1;
1185 178         244 my $wantcomment = 0;
1186 178 100       429 if ($key =~ /_COMMENT$/) {
1187 6         11 $wantvalue = 0;
1188 6         8 $wantcomment = 1;
1189             # Remove suffix
1190 6         22 $key =~ s/_COMMENT$//;
1191             }
1192              
1193             # if we are of type COMMENT we want to retrieve the comment only
1194             # if they're asking for $key_COMMENT.
1195 178         319 my $item;
1196             my $t_ok;
1197 178 100 100     1098 if ( $wantcomment || $key =~ /^(COMMENT)|(HISTORY)$/ || $key =~ /^END$/) {
      100        
1198 15         41 $item = ($self->itembyname($key))[0];
1199 15   100     57 $t_ok = (defined $item) && (defined $item->type);
1200 15 100 100     45 $wantvalue = 0 if ($t_ok && ($item->type eq 'COMMENT'));
1201             }
1202              
1203             # The END card is a special case. We always return " " for the value,
1204             # and undef for the comment.
1205 178 50 100     739 return ($wantvalue ? " " : undef)
    100 66        
      66        
1206             if ( ($t_ok && ($item->type eq 'END')) ||
1207             ((defined $item) && ($key eq 'END')) );
1208              
1209             # Retrieve all the values/comments. Note that we go through the entire
1210             # header for this in case of multiple matches
1211 176 100       572 my @values = ($wantvalue ? $self->value( $key ) : $self->comment($key) );
1212              
1213             # Return value depends on return context. If we have one value it does not
1214             # matter, just return it. In list context want all the values, in scalar
1215             # context join them all with a \n
1216             # Note that in a TIED hash we do not have access to the calling context
1217             # we are ALWAYS in scalar context.
1218 176         260 my @out;
1219              
1220             # Sometimes we want the array to remain an array
1221 176 100       378 if ($self->tiereturnsref) {
1222 4         10 @out = @values;
1223             } else {
1224              
1225             # Join everything together with a newline
1226             # BUT we are careful here to prevent stringification of references
1227             # at least for the case where we only have one value. We also must
1228             # handle the case where we have no value to return (without turning
1229             # it into a null string since that ruins autovivification of sub headers)
1230 172 100       350 if (scalar(@values) <= 1) {
1231 166         329 @out = @values;
1232             } else {
1233              
1234             # Multi values so join [protecting warnings from undef]
1235 6 50       13 @out = ( join("\n", map { defined $_ ? $_ : '' } @values) );
  17         49  
1236              
1237             # This is a hangover from the STORE (where we add a \ continuation
1238             # character to multiline strings)
1239 6 50       22 $out[0] =~ s/\\\n//gs if (defined($out[0]));
1240             }
1241             }
1242              
1243             # COMMENT cards get a newline appended.
1244             # (Whether this should happen is controversial, but it supports
1245             # the "just append a string to get a new COMMENT card" behavior
1246             # described in the documentation).
1247 176 100 100     437 if ($t_ok && ($item->type eq 'COMMENT')) {
1248 7         18 @out = map { $_ . "\n" } @out;
  12         36  
1249             }
1250              
1251             # If we have a header we need to tie it to another hash
1252 176   66     376 my $ishdr = ($t_ok && $item->type eq 'HEADER');
1253 176         318 for my $hdr (@out) {
1254 177 100 66     1085 if ((UNIVERSAL::isa($hdr, "Astro::FITS::Header")) || $ishdr) {
1255 11         15 my %header;
1256 11         44 tie %header, ref($hdr), $hdr;
1257             # Change in place
1258 11         28 $hdr = \%header;
1259             }
1260             }
1261              
1262             # Can only return a scalar
1263             # So return the first value if tiereturnsref is false.
1264             # (by this point, all the values should be joined together into the
1265             # first element anyway.)
1266 176         272 my $out;
1267 176 100 100     317 if ($self->tiereturnsref && scalar(@out) > 1) {
1268 2         4 $out = \@out;
1269             } else {
1270 174         299 $out = $out[0];
1271             }
1272              
1273 176         774 return $out;
1274             }
1275              
1276             # store key and value pair
1277             #
1278             # Multiple-line kludges (CED):
1279             #
1280             # * Array refs get handled gracefully by being put in as multiple cards.
1281             #
1282             # * Multiline strings get broken up and put in as multiple cards.
1283             #
1284             # * Extra-long strings get broken up and put in as multiple cards, with
1285             # an extra backslash at the end so that they transparently get put back
1286             # together upon retrieval.
1287             #
1288              
1289             sub STORE {
1290 22     22   7087 my ($self, $keyword, $value) = @_;
1291 22         38 my @values;
1292              
1293             # Recognize slash-delimited comments in value keywords. This is done
1294             # cheesily via recursion -- would be more efficient, but less readable,
1295             # to propagate the comment through the code...
1296              
1297             # I think this is fundamentally flawed. If I store a string "foo/bar"
1298             # in a hash and then read it back I expect to get "foo/bar" not "foo".
1299             # I can not be expected to know that this hash happens to be tied to
1300             # a FITS header that is trying to spot FITS item formatting. - TJ
1301              
1302             # Make sure that we do not stringify reference arguments by mistake
1303             # when looking from slashes
1304              
1305 22 100 66     200 if (defined $value && !ref($value) && $keyword !~ m/(_COMMENT$)|(^(COMMENT|HISTORY)$)/ and
      100        
      100        
1306             $value =~ s:\s*(?
1307             ) {
1308 2         7 my $comment = $1;
1309              
1310             # Recurse to store the comment. This is a direct (non-method) call to
1311             # keep this method monolithic. --CED 27-Jun-2003
1312 2         13 STORE($self,$keyword."_COMMENT",$comment);
1313              
1314             }
1315              
1316             # unescape (unless we are blessed)
1317 22 100 66     80 if (defined $value && !ref($value)) {
1318 17         32 $value =~ s:\\\\:\\:g;
1319 17         31 $value =~ s:\\\/:\/:g;
1320             }
1321              
1322             # skip the shenanigans for the normal case
1323             # or if we have an Astro::FITS::Header
1324 22 50 66     189 if (!defined $value) {
    100 100        
    100          
    100          
1325 0         0 @values = ($value);
1326              
1327             } elsif (UNIVERSAL::isa($value, "Astro::FITS::Header")) {
1328 1         3 @values = ($value);
1329              
1330             } elsif (ref $value eq 'HASH') {
1331             # Convert a hash to a Astro::FITS::Header
1332             # If this is a tied hash already just get the object
1333 3         6 my $tied = tied %$value;
1334 3 100 66     11 if (defined $tied && UNIVERSAL::isa($tied, "Astro::FITS::Header")) {
1335             # Just take the object
1336 1         3 @values = ($tied);
1337             } else {
1338             # Convert it to a hash
1339 2         5 @values = ( Astro::FITS::Header->new( Hash => $value ) );
1340             }
1341              
1342             } elsif ((ref $value eq 'ARRAY') || (length $value > 70) || $value =~ m/\n/s ) {
1343 3         6 my @val;
1344             # @val gets intermediate breakdowns, @values gets line-by-line breakdowns.
1345              
1346             # Change multiline strings into array refs
1347 3 100       18 if (ref $value eq 'ARRAY') {
    50          
    50          
1348 1         18 @val = @$value;
1349              
1350             } elsif (ref $value) {
1351 0         0 croak "Can't put non-array ref values into a tied FITS header\n";
1352              
1353             } elsif ( $value =~ m/\n/s ) {
1354 2         7 @val = split("\n",$value);
1355 2         5 chomp @val;
1356              
1357             } else {
1358 0         0 @val = $value;
1359             }
1360              
1361             # Cut up really long items into multiline strings
1362 3         5 my($val);
1363 3         8 foreach $val(@val) {
1364 8         18 while ((length $val) > 70) {
1365 0         0 push(@values,substr($val,0,69)."\\");
1366 0         0 $val = substr($val,69);
1367             }
1368 8         17 push(@values,$val);
1369             }
1370             } ## End of complicated case
1371             else {
1372              
1373              
1374              
1375 15         47 @values = ($value);
1376             }
1377              
1378             # Upper case the relevant item name
1379 22         48 $keyword = uc($keyword);
1380              
1381 22 100       60 if ($keyword eq 'END') {
1382             # Special case for END keyword
1383             # (drops value on floor, makes sure there is one END at the end)
1384 1         4 my @index = $self->index($keyword);
1385 1 50 33     6 if ( @index != 1 || $index[0] != $#{$self->allitems}) {
  0         0  
1386 1         2 my $i;
1387 1         6 while (defined($i = shift @index)) {
1388 0         0 $self->remove($i);
1389             }
1390             }
1391 1 50       3 unless( @index ) {
1392 1         12 my $endcard = new Astro::FITS::Header::Item(Keyword=>'END',
1393             Type=>'END',
1394             Value=>1);
1395 1         7 $self->insert( scalar ($self->allitems) , $endcard );
1396             }
1397 1         4 return;
1398              
1399             }
1400              
1401 21 100       55 if ($keyword eq 'SIMPLE') {
1402             # Special case for SIMPLE keyword
1403             # (sets value correctly, makes sure there is one SIMPLE at the beginning)
1404 1         6 my @index = $self->index($keyword);
1405 1 50 33     8 if ( @index != 1 || $index[0] != 0) {
1406 1         54 my $i;
1407 1         39 while (defined ($i=shift @index)) {
1408 0         0 $self->remove($i);
1409             }
1410             }
1411 1 50       8 unless( @index ) {
1412 1         19 my $simplecard = new Astro::FITS::Header::Item(Keyword=>'SIMPLE',
1413             Value=>$values[0],
1414             Type=>'LOGICAL');
1415 1         9 $self->insert(0, $simplecard);
1416             }
1417 1         6 return;
1418             }
1419              
1420              
1421             # Recognise _COMMENT
1422 20         31 my $havevalue = 1;
1423 20 100       46 if ($keyword =~ /_COMMENT$/) {
1424 3         10 $keyword =~ s/_COMMENT$//;
1425 3         6 $havevalue = 0;
1426             }
1427              
1428 20         47 my @items = $self->itembyname($keyword);
1429              
1430             ## Remove extra items if necessary
1431 20 100       48 if (scalar(@items) > scalar(@values)) {
1432 2         5 my(@indices) = $self->index($keyword);
1433 2         4 my($i);
1434 2         7 for $i (1..(scalar(@items) - scalar(@values))) {
1435 3         8 $self->remove( $indices[-$i] );
1436             }
1437             }
1438              
1439             ## Allocate new items if necessary
1440 20         56 while (scalar(@items) < scalar(@values)) {
1441              
1442 16         58 my $item = new Astro::FITS::Header::Item(Keyword=>$keyword,Value=>undef);
1443             # (No need to set type here; Item does it for us)
1444              
1445 16         60 $self->insert(-1,$item);
1446 16         40 push(@items,$item);
1447             }
1448              
1449             ## Set values or comments
1450 20         41 my($i);
1451 20         47 for $i(0..$#values) {
1452 25 100       76 if ($Astro::FITS::Header::COMMENT_FIELD{$keyword}) {
    100          
1453 6         15 $items[$i]->type('COMMENT');
1454 6         12 $items[$i]->comment($values[$i]);
1455             } elsif (! $havevalue) {
1456             # This is actually just changing the comment
1457 3         21 $items[$i]->comment($values[$i]);
1458             } else {
1459 16 100 100     97 $items[$i]->type( (($#values > 0) || ref $value) ? 'STRING' : undef);
1460              
1461 16         81 $items[$i]->value($values[$i]);
1462 16 100       94 $items[$i]->type("STRING") if($#values > 0);
1463             }
1464             }
1465             }
1466              
1467              
1468             # reports whether a key is present in the hash
1469             # SUBHEADERS only exist if there are subheaders
1470             sub EXISTS {
1471 12     12   2825 my ($self, $keyword) = @_;
1472 12         26 $keyword = uc($keyword);
1473              
1474 12 100       33 if ($keyword eq 'SUBHEADERS') {
1475 3 100       7 return ( scalar(@{$self->subhdrs}) > 0 ? 1 : 0);
  3         9  
1476             }
1477              
1478 9 100       12 if ( !exists( ${$self->{LOOKUP}}{$keyword} ) ) {
  9         28  
1479 2         10 return undef;
1480             }
1481              
1482             # if we are being asked for a keyword that is associated with a COMMENT or BLANK
1483             # type we return FALSE for existence. An undef type means we have to assume a valid
1484             # item with unknown type
1485 7 50       11 if ( exists( ${$self->{LOOKUP}}{$keyword} ) ) {
  7         17  
1486 7         11 my $item = ${$self->{HEADER}}[${$self->{LOOKUP}}{$keyword}[0]];
  7         17  
  7         11  
1487 7         19 my $type = $item->type;
1488 7 100 66     40 return undef if (defined $type && ($type eq 'COMMENT' || $type eq 'BLANK') );
      66        
1489             }
1490              
1491 6         20 return 1;
1492              
1493             }
1494              
1495             # deletes a key and value pair
1496             sub DELETE {
1497 1     1   738 my ($self, $keyword) = @_;
1498 1         4 return $self->removebyname($keyword);
1499             }
1500              
1501             # empties the hash
1502             sub CLEAR {
1503 3     3   1241 my $self = shift;
1504 3         78 $self->{HEADER} = [ ];
1505 3         26 $self->{LOOKUP} = { };
1506 3         7 $self->{LASTKEY} = undef;
1507 3         20 $self->{SEENKEY} = undef;
1508 3         15 $self->{SUBHDRS} = [ ];
1509             }
1510              
1511             # implements keys() and each()
1512             sub FIRSTKEY {
1513 9     9   1140 my $self = shift;
1514 9         17 $self->{LASTKEY} = 0;
1515 9         19 $self->{SEENKEY} = {};
1516 9 100       13 return $self->_check_for_subhdr() unless @{$self->{HEADER}};
  9         30  
1517 8         14 return ${$self->{HEADER}}[0]->keyword();
  8         28  
1518             }
1519              
1520             # implements keys() and each()
1521             sub NEXTKEY {
1522 147     147   306 my ($self, $keyword) = @_;
1523              
1524             # abort if the number of keys we have served equals the number in the
1525             # header array. One wrinkle is that if we have SUBHDRS we want to go
1526             # round one more time
1527              
1528 147 100       210 if ($self->{LASTKEY}+1 == scalar(@{$self->{HEADER}})) {
  147         283  
1529 10         23 return $self->_check_for_subhdr();
1530             }
1531              
1532             # Skip later lines of multi-line cards since the tie interface
1533             # will return all the lines for a single keyword request.
1534 137         186 my($a);
1535             do {
1536 139         169 $self->{LASTKEY} += 1;
1537 139         214 $a = $self->{HEADER}->[$self->{LASTKEY}];
1538             # Got to end of header if we do not have $a
1539 139 50       376 return $self->_check_for_subhdr() unless defined $a;
1540 137         180 } while ( $self->{SEENKEY}->{$a->keyword});
1541 137         268 $a = $a->keyword;
1542              
1543 137         232 $self->{SEENKEY}->{$a} = 1;
1544 137         389 return $a;
1545             }
1546              
1547             # called if we have run out of normal keys
1548             # args: $self Returns: undef or "SUBHEADER"
1549             sub _check_for_subhdr {
1550 11     11   14 my $self = shift;
1551 11 100 100     20 if (scalar(@{ $self->subhdrs}) && !$self->{SEENKEY}->{SUBHEADERS}) {
  11         22  
1552 2         5 $self->{SEENKEY}->{SUBHEADERS} = 1;
1553 2         7 return "SUBHEADERS";
1554             }
1555 9         37 return undef;
1556             }
1557              
1558              
1559             # garbage collection
1560             # sub DESTROY { }
1561              
1562             # T I M E A T T H E B A R --------------------------------------------
1563              
1564             =head1 SEE ALSO
1565              
1566             C, C,
1567             C, C.
1568              
1569             =head1 COPYRIGHT
1570              
1571             Copyright (C) 2007-2011 Science and Technology Facilties Council.
1572             Copyright (C) 2001-2007 Particle Physics and Astronomy Research Council
1573             and portions Copyright (C) 2002 Southwest Research Institute.
1574             All Rights Reserved.
1575              
1576             This program is free software; you can redistribute it and/or modify it under
1577             the terms of the GNU General Public License as published by the Free Software
1578             Foundation; either version 3 of the License, or (at your option) any later
1579             version.
1580              
1581             This program is distributed in the hope that it will be useful,but WITHOUT ANY
1582             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1583             PARTICULAR PURPOSE. See the GNU General Public License for more details.
1584              
1585             You should have received a copy of the GNU General Public License along with
1586             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
1587             Place,Suite 330, Boston, MA 02111-1307, USA
1588              
1589             =head1 AUTHORS
1590              
1591             Alasdair Allan Eaa@astro.ex.ac.ukE,
1592             Tim Jenness Et.jenness@jach.hawaii.eduE,
1593             Craig DeForest Edeforest@boulder.swri.eduE,
1594             Jim Lewis Ejrl@ast.cam.ac.ukE,
1595             Brad Cavanagh Eb.cavanagh@jach.hawaii.eduE
1596              
1597             =cut
1598              
1599             package Astro::FITS::HeaderCollection;
1600              
1601 10     10   52178 use 5.006;
  10         59  
1602 10     10   59 use warnings;
  10         32  
  10         687  
1603 10     10   80 use strict;
  10         24  
  10         403  
1604 10     10   71 use Carp;
  10         62  
  10         8907  
1605              
1606             our $VERSION;
1607             $VERSION = '3.09';
1608              
1609             # Class wrapper for subhdrs tie. Not (yet) a public interface
1610             # we simply need a class that we can tie the subhdrs array to.
1611              
1612             sub TIEARRAY {
1613 2     2   9 my ($class, $container) = @_;
1614             # create an object, but we want to avoid blessing the actual
1615             # array into this class
1616 2         17 return bless { SUBHDRS => $container }, $class;
1617             }
1618              
1619             # must return a new tie
1620             sub FETCH {
1621 7     7   33 my $self = shift;
1622 7         11 my $index = shift;
1623              
1624 7         20 my $arr = $self->{SUBHDRS};
1625 7 50 33     34 if ( $index >= 0 && $index <= $#$arr ) {
1626 7         21 return $self->_hdr_to_tie( $arr->[$index] );
1627             } else {
1628 0         0 return undef;
1629             }
1630             }
1631              
1632             sub STORE {
1633 2     2   4 my $self = shift;
1634 2         3 my $index = shift;
1635 2         4 my $value = shift;
1636              
1637 2         4 my $hdr = $self->_tie_to_hdr( $value );
1638 2         8 $self->{SUBHDRS}->[$index] = $hdr;
1639             }
1640              
1641             sub FETCHSIZE {
1642 4     4   878 my $self = shift;
1643 4         7 return scalar( @{ $self->{SUBHDRS} });
  4         21  
1644             }
1645              
1646             sub STORESIZE {
1647 0     0   0 croak "Tied STORESIZE for SUBHDRS not yet implemented\n";
1648             }
1649              
1650       0     sub EXTEND {
1651              
1652             }
1653              
1654             sub EXISTS {
1655 0     0   0 my $self = shift;
1656 0         0 my $index = shift;
1657 0         0 my $arr = $self->{SUBHDRS};
1658              
1659 0 0 0     0 return 0 if $index > $#$arr || $index < 0;
1660 0 0       0 return 1 if defined $self->{SUBHDRS}->[$index];
1661 0         0 return 0;
1662             }
1663              
1664             sub DELETE {
1665 0     0   0 my $self = shift;
1666 0         0 my $index = shift;
1667 0         0 $self->{SUBHDRS}->[$index] = undef;
1668             }
1669              
1670             sub CLEAR {
1671 1     1   3 my $self = shift;
1672 1         2 @{ $self->{SUBHDRS} } = ();
  1         4  
1673             }
1674              
1675             sub PUSH {
1676 1     1   598 my $self = shift;
1677 1         3 my @list = @_;
1678              
1679             # convert
1680 1         2 @list = map { $self->_tie_to_hdr($_) } @list;
  1         3  
1681 1         6 push(@{ $self->{SUBHDRS} }, @list);
  1         4  
1682             }
1683              
1684             sub POP {
1685 1     1   333 my $self = shift;
1686 1         2 my $popped = pop( @{ $self->{SUBHDRS} } );
  1         2  
1687 1         3 return $self->_hdr_to_tie($popped);
1688             }
1689              
1690             sub SHIFT {
1691 1     1   2 my $self = shift;
1692 1         2 my $shifted = shift( @{ $self->{SUBHDRS} } );
  1         2  
1693 1         4 return $self->_hdr_to_tie($shifted);
1694             }
1695              
1696             sub UNSHIFT {
1697 1     1   625 my $self = shift;
1698 1         2 my @list = @_;
1699              
1700             # convert
1701 1         3 @list = map { $self->_tie_to_hdr($_) } @list;
  1         3  
1702 1         2 unshift(@{ $self->{SUBHDRS} }, @list);
  1         4  
1703              
1704             }
1705              
1706             # internal mappings
1707              
1708             # Given an Astro::FITS::Header object, return the thing that
1709             # should be returned to the user of the tie
1710             sub _hdr_to_tie {
1711 9     9   14 my $self = shift;
1712 9         14 my $hdr = shift;
1713              
1714 9 50       21 if (defined $hdr) {
1715 9         12 my %header;
1716 9         36 tie %header, ref($hdr), $hdr;
1717 9         83 return \%header;
1718             }
1719 0         0 return undef;
1720             }
1721              
1722             # convert an input argument as either a Astro::FITS::Header object
1723             # or a hash, to an internal representation (an A:F:H object)
1724             sub _tie_to_hdr {
1725 4     4   7 my $self = shift;
1726 4         5 my $value = shift;
1727              
1728 4 50       16 if (UNIVERSAL::isa($value, "Astro::FITS::Header")) {
    50          
1729 0         0 return $value;
1730             } elsif (ref($value) eq 'HASH') {
1731 4         8 my $tied = tied %$value;
1732 4 100 66     19 if (defined $tied && UNIVERSAL::isa($tied, "Astro::FITS::Header")) {
1733             # Just take the object
1734 3         10 return $tied;
1735             } else {
1736             # Convert it to a hash
1737             my @items = map { new Astro::FITS::Header::Item( Keyword => $_,
1738 1         4 Value => $value->{$_}
1739 1         2 ) } keys (%{$value});
  1         4  
1740              
1741             # Create the Header object.
1742 1         3 return new Astro::FITS::Header( Cards => \@items );
1743              
1744             }
1745             } else {
1746 0           croak "Do not know how to store '$value' in a SUBHEADER\n";
1747             }
1748             }
1749              
1750             # L A S T O R D E R S ------------------------------------------------------
1751              
1752             1;