#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------

package Common::Parser;

#
# Common::Parser
#
# This class provides an interface to access various formats
# of datafiles.  Once a file is opened using the Parse method
# the file can be traversed using the iterator.  The iterator
# can return a collection of data using the next method.
# Currently it is designed to read tabular data from Excel,
# CVS, and TSV files and the collection return is a Row.
#
# Usage:
#
#   my $parser = new Common::Parser( filename => $file );
#   my $iterator = $parser->Parse();
#
# or a shorthand
#
#   my $iterator = Common::Parser->Parse( filename => $file );
#

use strict;

use lib '/app/tools/common/lib';
use Common::Assert;

###
#   Defines the classs that can parse data.  Valid iterators
###
sub _formats {
    qw(
      Common::Parser::Iterator::Excel::XLSX
      Common::Parser::Iterator::Excel::XLS
      Common::Parser::Iterator::Text::CSV
      Common::Parser::Iterator::Text::TSV
    );
}

# new
#
# my $parser = new Common::Parser( filename => $file )
#
# Constructor.  Filename is a required field.
#
sub new {
    my $class = shift;

    my $self = {};
    bless $self, $class;

    return $self->_init(@_);
}

# Parse
#
# my $iterator = Common::Parser( filename => $file )
#
# Returns a _new_ Common::Parser::Iterator referencing the file passed in.
# Filename is only required if:
#   1. This method is called without a blessed reference.
#   2. Filename isn't passed to the constructor.
#
sub Parse {
    my $class = shift;
    my %args  = @_;

    my $self;

    if ( ref($class) ) {
        $self = $class;
    } else {
        $self = $class->new();
    }

    return $self->_open( $args{filename} );
}

# _init
#
# Store the filename during construction.
sub _init {
    my ( $self, %args ) = @_;

    $self->{_filename} = $args{filename};

    return $self;
}

# _open
#
# $iterator = $self->_open( $filename );
#
# Attempt to open a file using iterators.  Each iterator class, defined in the
# _iterators method, will be used to attempt to read the file (facilitated by
# the CanParse static method).  If we find an iterator that can read the file
# we create a new iterator object and return that.
#
# return:
#
#    Return a Common::Iterator subclass if the file is a known format
#    otherwise return undef;
#
sub _open {
    my $self = shift;
    my $filename = shift || $self->{_filename};
    my $format;

    $self->{_error}    = undef;
    $self->{_filename} = $filename;

    assert( $filename,    "File required" );
    assert( -r $filename, "Can not read file: $filename" );

    foreach my $formatClass ( $self->_formats() ) {
        eval "require $formatClass";
        die $@ if ($@);

        if ( my $iterator = $formatClass->CanParse( filename => $filename ) ) {
            assert( ref($iterator), "$formatClass" );
            $self->{_iterator} = $iterator;
            return $iterator;
        }
    }

    $self->{_error} = "Unknown Format";
    return;
}

###
#   Accessors
###
sub filename { shift->{_filename}; }

###
1;    # Play nicely.
###
