Util::Catalog (version $)


package Util::Catalog;
##############################################################################
#
# DESCRIPTION: This class is useful for generating catalogs of files.
# DESCRIPTION: Calling the "make" method will create a FITS output catalog
# DESCRIPTION: with the standard format. The genral user does not need
# DESCRIPTION: to worry about any of the other methods.
# DESCRIPTION: 
# DESCRIPTION: Certain catalog information is accumulated in the class data.
# DESCRIPTION: This makes it more efficient to generate more than one catalog
# DESCRIPTION: containing identical files. 
# DESCRIPTION: It also allows the "all_files" and "fits_files" methods to
# DESCRIPTION: give lists
# DESCRIPTION: of files in the union of all catalogs created so far.
# DESCRIPTION: 
# DESCRIPTION: Sub-classes may change the format of the catalog file
# DESCRIPTION: by overriding some of the methods. The Util::HTMLcatalog class
# DESCRIPTION: does this.
#
# HISTORY
# HISTORY: $Log: Catalog.pm,v $
# HISTORY: Revision 1.3  2014/02/27 07:01:06  apsop
# HISTORY: VERSION header now shows CVS Revision
# HISTORY:
# HISTORY: Revision 1.2  2006/08/01 20:35:34  apsop
# HISTORY: Add in CVS history indicator.
# HISTORY:
# HISTORY:
# HISTORY: 2005-05-27
# HISTORY: Add capability a file type to have more than one file class
# HISTORY:
#
# VERSION: $Revision: 1.3 $
#
##############################################################################

use Util::Stool;
use strict;

my $FILENAME;
my $LOG;
my $isFITS;

my $HEADER_TEMPLATE;
my $KEYWORD_TEMPLATE;

my %SIZE;
my %FORMAT;

my %FILES_OF_TYPE;
my %ALL_FILES;
my %FITS_FILES;

my %FUTURE_FILES;

####################################
# initialize some of the class data
####################################
sub BEGIN {

    #####################################
    # initialize variables
    #####################################
    %SIZE=();
    %FORMAT=();
    %FILES_OF_TYPE=();
    %ALL_FILES=();
    %FITS_FILES=();

    

    ####################################################
    # text for header template file
    ####################################################
    $HEADER_TEMPLATE ="FILENAME 64A\n";
    $HEADER_TEMPLATE.="FORMAT   16A\n";
    $HEADER_TEMPLATE.="TYPE     32A\n";
    $HEADER_TEMPLATE.="FILECLAS 32A\n";
    $HEADER_TEMPLATE.="DESCRIP  64A\n";
    $HEADER_TEMPLATE.="FILESIZE  1J kilobytes\n";
    $HEADER_TEMPLATE.="ARCHSIZE  1J kilobytes\n";
    $HEADER_TEMPLATE.="CHECKSUM  1J\n";
    $HEADER_TEMPLATE.="GZIP_CRC  8A\n";

}

#########################
# constructor
#########################
sub new { #(catalog_type)
    my $proto = shift;
    my $class = ref($proto) || $proto;

    #######################################
    # be sure we are initialized
    #######################################
    unless( $FILENAME) {
        print STDERR "Util::Catalog not initialized properly";
        exit 1;
    }

    #####################################
    # initialize some variables
    #####################################
    my $self={};
    $self->{CAT_TYPE}=shift;
    $self->{CAT_NAME}=$FILENAME->get($self->{CAT_TYPE});
    $self->{DATA}="";

    #########################################################
    # put references to these variables in $self so that they
    # can be accessed by sub-classes
    #########################################################

    $self->{FILENAME}=$FILENAME;
    $self->{LOG}=$LOG;

    $self->{HEADER_TEMPLATE}=\$HEADER_TEMPLATE;
    $self->{KEYWORD_TEMPLATE}=\$KEYWORD_TEMPLATE;

    $self->{SIZE}=\%SIZE;
    $self->{FORMAT}=\%FORMAT;

    $self->{FILES_OF_TYPE}=\%FILES_OF_TYPE;
    $self->{ALL_FILES}=\%ALL_FILES;
    $self->{FITS_FILES}=\%FITS_FILES;

    $self->{FUTURE_FILES}=\%FUTURE_FILES;

    bless($self,$class);
    return $self;

}


###################################
# ACCESSORS:
###################################

##############################################################
# set or return the filename generator object for this class
##############################################################
sub filename {
    my $self=shift;
 
    if(@_) {
        #####################################
        # we are setting the filename object
        #####################################
        $FILENAME=shift;

        ######################################################
        # text for keywords template file
        ######################################################
        my $origin=$FILENAME->procpar()->read("origin");
        my $seq   =$FILENAME->sequence();
        my $ver   =$FILENAME->version();

        $KEYWORD_TEMPLATE ="ORIGIN=$origin / Place this file was created\n";

        $LOG=$FILENAME->log();

    } # end if setting FILENAME object

    return $FILENAME;
}

#########################################################################
# set the files which we should not expect to exist yet when we
# are making the catalog.
#########################################################################
sub future_files { #(type=>format, type=>format...)
    my $self=shift;
    
    %FUTURE_FILES=@_;

    my $type;
    foreach $type (keys %FUTURE_FILES ) {

        $FORMAT{$FILENAME->get($type)}=$FUTURE_FILES{$type};
        $SIZE{$FILENAME->get($type)}=0;
    }
}
    
#############################################################
# returns a list of all FITS format files appearing in all
# catalogs created so far.
#############################################################
sub fits_files {
    my $self=shift;

    return keys %FITS_FILES;
}

#############################################################
# returns a list of all files appearing in all
# catalogs created so far.
#############################################################
sub all_files {
    my $self=shift;
    return keys %ALL_FILES;
}

###################
# METHODS:
###################

######################################################
# determine the format (FITS, ASCII, etc.) of a file.
######################################################
sub format {
    my $self=shift;
    my $file=shift;

    ######################################################
    # determine the file format if we don't know it already
    ######################################################
    unless( $FORMAT{$file} ) {
        ######################################################
        # create the isFITS tool if it doesn't already exist
        ######################################################
        unless($isFITS) { 
            $isFITS=Util::Stool->new("isFITS")
                               ->verbose(0) 
        }

        ######################
        # determine the format
        ######################
        $FORMAT{$file}=$isFITS->command_line($file)
                              ->run()
                              ->stdout();
        chomp($FORMAT{$file});

    }

    ############################
    # return the file size
    ############################
    return $FORMAT{$file};

} # end of format method


#############################################
# determine the size of a file in kilobytes
#############################################
sub size {
    my $self=shift;
    my $file=shift;

    ######################################################
    # calculate the file size if we don't know it already
    ######################################################
    unless( defined $SIZE{$file} ) {

        $SIZE{$file}=int((-s $file)/1024+.5);

    }

    ############################
    # return the file size
    ############################
    return $SIZE{$file};

} # end of size method


######################################
# add a file to the catalog
######################################
sub add_file {
    my $self=shift;
    my $file=shift;
    my $type=shift;
    my $classes=shift;
    my $description=shift;

#print "file=|$file| type=|$type| class=|$class| desc=|$description|\n";

    my $format=$self->format($file);
    my $size  =$self->size($file);

    $ALL_FILES{$file}=$type;
    if($format eq 'FITS' ) { $FITS_FILES{$file}=$type }
    
    foreach my $class (@$classes){
      $self->{DATA}.="$file $format $type $class '$description' ";
      $self->{DATA}.="$size 0 0 'ffffffff'\n";
    }

}

#######################################
# return all the files of a given type
#######################################
sub files_of_type {
    my $self=shift;
    my $type=shift;

    unless($FILES_OF_TYPE{$type} ) {
        #############################################
        # need to determine the files for this type
        ############################################

        if($FUTURE_FILES{$type}) {
            ##########################################
            # the file of this type may not exist yet
            ##########################################
            my $file=$FILENAME->get($type);
 
            if( -f $file && ! $SIZE{file} ) {
                ##########################################
                # the file has been created already
                # but has zero size, so set its size as 
                # unknown so it will be determined later
                ##########################################
                $SIZE{$file}=undef;
            }

            $FILES_OF_TYPE{$type}=[$file];

        } else {
            ######################################################
            # only add the files of this type which already exist
            ######################################################
            $FILES_OF_TYPE{$type}=[];
            foreach ($FILENAME->any($type)) {
                if(-f ) {push @{$FILES_OF_TYPE{$type}}, ($_) }
            }
        } # end if files should exist already
    } # end if we needed to determine the files for this type

    return @{$FILES_OF_TYPE{$type}};
}


###################################################
# add all the files of a given type to the catalog
###################################################
sub add_type {
    my $self=shift;
    my $type=shift;

    my $description=$FILENAME->description($type);
    my $classes    =$FILENAME->class($self->{CAT_TYPE},$type);

    my $file;
    foreach $file ($self->files_of_type($type) ) {
        $self->add_file($file,$type,$classes,$description);
    }

}

#########################################################
# add all the files of all the file types to the catalog
#########################################################
sub fill {
    my $self=shift;

    my $type;
    foreach $type ($FILENAME->types($self->{CAT_TYPE}) ) {

        $self->add_type($type);
    }
}

###############################################################
# actually write the catalog file
###############################################################
sub close {
    my $self=shift;

    ##################################
    # create the template files
    ##################################
    my $header_template="$self->{CAT_TYPE}_header_template.tmp";
    open  HEADER, ">$header_template";
    print HEADER $HEADER_TEMPLATE;
    close HEADER;

    my $keyword_template="$self->{CAT_TYPE}_keyword_template.tmp";
    open  KEYWORD, ">$keyword_template";
    print KEYWORD $KEYWORD_TEMPLATE;
    close KEYWORD;

    my $data_template="$self->{CAT_TYPE}_data_template.tmp";
    open  DATA, ">$data_template";
    print DATA $self->{DATA};
    close DATA;

    #################################################################
    # run fcreate - destroy it explicitly to be sure the parfile is
    # not hanging around later when we look for leftover files
    #################################################################
    unlink $self->{CAT_NAME};

    Util::Ftool->new("fcreate")
               ->params({outfile  => $self->{CAT_NAME},
                         datafile =>    $data_template,
                         cdfile   =>  $header_template,
                         headfile => $keyword_template,
                         tbltype  => "binary",
                         nskip    => 0,
                         nrows    => 0,
                         history  => "no",
                         morehdr  => 0,
                         extname  => "CATALOG",
                         clobber  => "yes" })
              ->run()
              ->DESTROY();

    #################################
    # clean up the temporary files
    #################################
    unlink    $data_template;
    unlink  $header_template;
    unlink $keyword_template;

} # end of close method


########################################
# fill and close the catalog
########################################
sub make {
    my $self=shift;

    $LOG->entry("Creating $self->{CAT_NAME}");

    $self->fill();
    $self->close();

    return $self;
}


###########################################################
# add the name of the catalog to the parfile
###########################################################
sub register_in_parfile {
    my $self=shift;
    my $param=shift || $self->{CAT_TYPE};

    $FILENAME->jobpar()->set({$param=>$self->{CAT_NAME}});

}


1;