The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl
# vim:ts=8 sw=4 sts=4 ai
require v5.6.1;
use strict;
use warnings;

=head1 NAME

xml2fv - convert an XML file into Tie::FieldVals data.

=head1 VERSION

This describes version B<0.6203> of xml2fv.

=cut

our $VERSION = '0.6203';

=head1 SYNOPSIS

xml2fv --help | --manpage | --version

xml2fv I<xmlfile> [ I<outfile> ]

=head1 DESCRIPTION

This script converts an XML file into Tie::FieldVals data.

=head1 OPTIONS

=over

=item --help

Print help message and exit.

=item --manpage

Print the full help documentation (manual page) and exit.

=item --verbose

Print informational messages.

=item --version

Print version information and exit.

=back

=head1 FILE FORMATS

=head2 XML FORMAT

The format of the input XML file is as follows:

    <fv_data>
        <record>
	   <Field>Value</Field>
	    <AnotherField>AnotherValue</AnotherField>
	    ...
	</record>
	...
    </fv_data>

Each field name is given its own tag, and the element contains
the value for that field.

=head2 FieldVals Format

The output data file is in the form of Field:Value pairs, with each
record separated by a line with '=' on it.

See L<Tie::FieldVals/FILE FORMAT> for more information.

=head1 REQUIRES

    Getopt::Long
    Pod::Usage
    Getopt::ArgvFile
    Data::Dumper
    Tie::FieldVals::Row

=head1 SEE ALSO

perl(1)
Getopt::Long
Getopt::ArgvFile
Pod::Usage

=cut

use Getopt::Long 2.34;
use Getopt::ArgvFile qw(argvFile);
use Pod::Usage;
use Data::Dumper;
use Tie::FieldVals::Row;

#========================================================
# Subroutines
sub init_data ($) {
    my $data_ref = shift;

    # options
    my %default_conf = ();
    $default_conf{debug} = 0;
    $default_conf{manpage} = 0;
    $default_conf{version} = 0;
    $default_conf{verbose} = 0;
    $default_conf{outfile} = '-';
    $default_conf{xmlfile} = '';
    $data_ref->{options} = \%default_conf;
} # init_data

sub process_args ($) {
    my $data_ref = shift;

    my $ok = 1;

    argvFile(home=>1,current=>1,startupFilename=>'.xml2fvrc');

    pod2usage(2) unless @ARGV;

    my $op = new Getopt::Long::Parser;
    $op->configure(qw(auto_version auto_help));
    $op->getoptions($data_ref->{options},
		    'verbose!',
		    'manpage',
		    'debug!',
		    'xmlfile=s',
		    'outfile=s',
		   ) or pod2usage(2);

    if ($data_ref->{options}->{'manpage'})
    {
	pod2usage({ -message => "$0 version $VERSION",
		    -exitval => 0,
		    -verbose => 2,
	    });
    }

} # process_args

sub convert_file ($) {
    my $data_ref = shift;

    my $outfile = $data_ref->{options}->{outfile};

    print STDERR "xmlfile: ", $data_ref->{options}->{xmlfile},
	" outfile: ", $outfile,
	"\n" if ($data_ref->{options}->{verbose});
    if ($data_ref->{options}->{debug})
    {
	print STDERR Data::Dumper->Dump([$data_ref], [qw(xml2fv)]);
    }

    my $outhandle = \*STDOUT;
    if ($outfile ne '-')
    {
	open(OUTFILE, ">$outfile") || die "Can't open '$outfile' for writing.";
	$outhandle = \*OUTFILE;
    }
    # open the XML file and go through it, with </record>
    # as the record separator
    my $recsep = '</record>';
    my $count = 0;
    my @field_names = ();
    {
	local $/ = $recsep;
	open(FILE, $data_ref->{options}->{xmlfile})
	    or die "cannot open ", $data_ref->{options}->{xmlfile};
	while (my $rec = <FILE>)
	{
	    warn $rec, "\n" if $data_ref->{options}->{debug};
	    if ($rec =~ /<record>/)
	    {
		my %row = ();
		my $row_obj;
		if (@field_names)
		{
		    $row_obj = tie %row, 'Tie::FieldVals::Row',
			fields=>\@field_names;
		    $row_obj->set_from_xml_string($rec);
		}
		else # get the field names from the first record
		{
		    $row_obj = tie %row, 'Tie::FieldVals::Row',
			fields=>[qw(dummy)];
		    $row_obj->set_from_xml_string($rec,
			override_keys=>1);
		    @field_names = @{$row_obj->field_names()};
		    # print the initial empty record
		    foreach my $fn (@field_names)
		    {
			print $outhandle "$fn:\n";
		    }
		    print $outhandle "=\n";
		}
		# print the current record
		print $outhandle $row_obj->get_as_string();
		print $outhandle "\n=\n";
		$count++;
	    }
	}
	close(FILE);
    }

    print STDERR "$count records processed\n" if ($data_ref->{options}->{verbose});

    if ($outfile ne '-')
    {
	close(OUTFILE);
    }
    print STDERR "done!\n" if ($data_ref->{options}->{verbose});
} # convert_file

#========================================================
# Main

MAIN: {
    my %data = ();

    init_data(\%data);
    process_args(\%data);

    # first argument is the xmlfile
    if (@ARGV)
    {
	$data{options}->{xmlfile} = shift @ARGV;
    }
    if (!$data{options}->{xmlfile})
    {
	print STDERR "$0 no xmlfile\n";
	return 1;
    }
    if (!-e $data{options}->{xmlfile})
    {
	print STDERR "$0 xmlfile not found\n";
	return 1;
    }

    # remaining argument is the output file
    if (@ARGV)
    {
	$data{options}->{outfile} = shift @ARGV;
    }
    convert_file(\%data);
}

=head1 BUGS

Please report any bugs or feature requests to the author.

=head1 AUTHOR

    Kathryn Andersen (RUBYKAT)
    perlkat AT katspace dot com
    http://www.katspace.com

=head1 COPYRIGHT AND LICENCE

Copyright (c) 2004 by Kathryn Andersen

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.


=cut

__END__