The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#
# This file is part of TBX-Checker
#
# This software is copyright (c) 2013 by Alan K. Melby.
#
# This is free software; you can redistribute it and/or modify it under
# the same terms as the Perl 5 programming language system itself.
#
package TBX::Checker;
use strict;
use warnings;
use autodie;
use File::ShareDir 'dist_dir';
use Exporter::Easy (
	OK => [ qw(check) ],
);
use Path::Tiny;
use Carp;
use feature 'state';
use Capture::Tiny 'capture_merged';
our $VERSION = '0.03'; # VERSION

my $TBXCHECKER = path( dist_dir('TBX-Checker'),'tbxcheck-1_2_9.jar' );

# ABSTRACT: Check TBX validity using TBXChecker

#When run as a script instead of used as a module: check the input file and print the results
_run(@ARGV) unless caller;
sub _run {
	my ($tbx) = @_;
	my ($passed, $messages) = check($tbx);
	($passed && print 'ok!')
		or print join (qq{\n}, @$messages);
	return;
}


sub check {
	my ($data, @args) = @_;

    croak 'missing data argument. Usage: TBX::Checker::check($data, %args)'
        unless $data;

    my $file = _get_file($data);
    #due to TBXChecker bug, file must be relative to cwd
    my $rel_file = $file->relative;
    my $arg_string = _get_arg_string(@args);

    my $command = qq{java -cp ".;$TBXCHECKER" org.ttt.salt.Main } .
        qq{$arg_string "$rel_file"};

	# capture STDOUT and STDERR from jar call into $output
	my $output = capture_merged {system($command)};
	my @messages = split /\v+/, $output;
	my $valid = _is_valid(\@messages);
	return ($valid, \@messages);
}

# get a Path::Tiny object for the file to give to the TBXChecker
sub _get_file {
    my ($data) = @_;
    my $file;
    #pointers are string data
    if(ref $data eq 'SCALAR'){
        $file = Path::Tiny->tempfile;
        #TODO: will this get encodings right?
        $file->append_raw($$data);
    #everything else should be string paths
    }else{
        $file = path($data);
        croak "$file doesn't exist!"
            unless $file->exists;
    }
    return $file;
}

# process arguments and return the command to be run and the file
# being processed (so temp files aren't destroyed by leaving scope)
sub _get_arg_string {
	my (%args) = @_;

	# check the parameters.
    # TODO: use a module or something for param checking
    state $allowed_params = [ qw(
        loglevel lang country variant system version environment) ];
	state $allowed_levels = [ qw(
		OFF SEVERE WARNING INFO CONFIG FINE FINER FINEST ALL) ];
    foreach my $param (keys %args){
        croak "unknown paramter: $param"
            unless grep { $_ eq $param } @$allowed_params;
    }
	if(exists $args{loglevel}){
		grep { $_ eq $args{loglevel} } @$allowed_levels
			or croak "Loglevel doesn't exist: $args{loglevel}";
	}
	$args{loglevel} ||= q{OFF};

	#combine the options into a string that TBXChecker will understand
	return join q{ }, map {"--$_=$args{$_}"} keys %args;
}

#return a boolean indicating the validity of the file, given the messages
#remove the message indicating that the file is valid (if it exists)
sub _is_valid {
	my ($messages) = @_;
	#locate index of "Valid file:" message
	my $index = 0;
	while($index < @$messages){
		last if $$messages[$index] =~ /^Valid file: /;
		$index++;
	}
	#if message not found, file was invalid
	if($index > $#$messages){
		return 0;
	}
	#remove message and return true
	splice(@$messages, $index, 1);
	return 1;
}

1;

__END__

=pod

=head1 NAME

TBX::Checker - Check TBX validity using TBXChecker

=head1 VERSION

version 0.03

=head1 SYNOPSIS

	use TBX::Checker qw(check);
	my ($passed, $messages) = check('/path/to/file.tbx');
	$passed && print 'ok!'
		or print join (qq{\n}, @$messages);

=head1 DESCRIPTION

This modules allows you to use the Java TBXChecker utility from Perl.
It has one function, C<check> which returns the errors found by the
TBXChecker (hopefully none!).

=head1 METHODS

=head2 C<check>

Checks the validity of the given TBX file. Returns 2 elements: a
boolean representing the validity of the input TBX, and an array reference
containing messages returned by TBXChecker.

Arguments: a string containing a TBX file path, or a string pointer containing
TBX data to be checked, followed by named arguments accepted by TBXChecker.
For example: C<check('file.tbx', loglevel => 'ALL')>. The allowed parameters
are listed below:

    loglevel      Increase level of output while processing.
                         OFF     => Error code only.
                         SEVERE  => Error code only.
                         WARNING => Valid or invalid message (default).
                         INFO    => Location of files used in processing.
                         CONFIG  => .
                         FINE    => .
                         FINER   => .
                         FINEST  => .
                         ALL     => All logging messages.
    lang           ISO-639 lowercase two-letter language code.
    country      ISO-3166 uppercase two-letter country code.
    variant
    system       System ID to use for relative paths in document.
                         Default: Uses the directory where the file is located.
    version       Displays version information and quits.
    environment    Adds the environmental conditions on startup to the messages.

Keep in mind that if you use a string pointer instead of a file name, all
relative URI's will be resolved from the current working directory.

=head1 SEE ALSO

The TBXChecker project is located on SourceForge in a
project called L<tbxutil|http://sourceforge.net/projects/tbxutil/>.

=head1 AUTHOR

Nathan Glenn <garfieldnate@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by Alan K. Melby.

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

=cut