The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Data::Report::Plugin::Text.pm -- Text plugin for Data::Report
# RCS Info        : $Id: Text.pm,v 1.10 2008/08/18 09:51:23 jv Exp $
# Author          : Johan Vromans
# Created On      : Wed Dec 28 13:21:11 2005
# Last Modified By: Johan Vromans
# Last Modified On: Mon Aug 18 11:46:04 2008
# Update Count    : 149
# Status          : Unknown, Use with caution!

package Data::Report::Plugin::Text;

use strict;
use warnings;
use base qw(Data::Report::Base);
use Carp;

################ User API ################

sub start {
    my $self = shift;
    $self->_argcheck(0);
    $self->SUPER::start;
    $self->_make_format;
    $self->{lines} = 0;
    $self->{page} = $=;
}

sub add {
    my ($self, $data) = @_;

    my $style = delete($data->{_style});
    if ( $style && !$self->_checkname($style) ) {
	croak("Invalid style name: \"$style\"");
    }
    $self->SUPER::add($data);

    $self->_checkhdr;

    my $skip_after = 0;
    my $line_after = 0;
    my $cancel_skip = 0;
    if ( $style and my $t = $self->_getstyle($style) ) {
	return	     if $t->{ignore};
	$self->_skip if $t->{skip_before};
	$skip_after   = $t->{skip_after};
	$self->_line if $t->{line_before};
	$line_after   = $t->{line_after};
	$cancel_skip  = $t->{cancel_skip};
    }
    $style = "*" unless defined($style);
    $self->_checkskip($cancel_skip);

    my @values;
    my @widths;
    my @indents;
    my $linebefore;
    my $lineafter;

    foreach my $col ( @{$self->_get_fields} ) {
	my $fname = $col->{name};
	my $t = $style ? $self->_getstyle($style, $fname) : {};
	next if $t->{ignore};

	push(@values, defined($data->{$fname}) ? $data->{$fname} : "");
	push(@widths, $col->{width});
	if ($col->{truncate} ) {
	    $values[-1] = substr($values[-1], 0, $widths[-1]);
	}

	# Examine style mods.
	my $indent = 0;
	my $wrapindent = 0;
	my $excess = 0;
	if ( $t ) {
	    $indent = $t->{indent} || 0;
	    $wrapindent = defined($t->{wrap_indent}) ? $t->{wrap_indent} : $indent;
	    croak("Row $style, column $fname, ".
		  "illegal value for indent property: $indent")
	      if $indent < 0 || $indent >= $self->_get_fdata->{$fname}->{width};
	    croak("Row $style, column $fname, ".
		  "illegal value for wrap_indent property: $wrapindent")
	      if $wrapindent < 0 || $wrapindent >= $self->_get_fdata->{$fname}->{width};
	    if ( $t->{line_before} ) {
		$linebefore->{$fname} =
		  ($t->{line_before} eq "1" ? "-" : $t->{line_before}) x $col->{width};
	    }
	    if ( $t->{line_after} ) {
		$lineafter->{$fname} =
		  ($t->{line_after} eq "1" ? "-" : $t->{line_after}) x $col->{width};
	    }
	    if ( $t->{excess} ) {
		$widths[-1] += 2;
	    }
	    if ( $t->{truncate} || $col->{truncate} ) {
		$values[-1] = substr($values[-1], 0, $widths[-1] - $indent);
	    }
	}
	push(@indents, [$indent, $wrapindent]);

    }

    if ( $linebefore ) {
	$linebefore->{_style} = "";
	$self->add($linebefore);
    }

    my @lines;
    while ( 1 ) {
	my $more = 0;
	my @v;
	foreach my $i ( 0..$#widths ) {
	    my ($ind, $wind) = @{$indents[$i]};
	    $ind = $wind if @lines;
	    my $maxw = $widths[$i] - $ind;
	    $ind = " " x $ind;
	    if ( length($values[$i]) <= $maxw ) {
		push(@v, $ind.$values[$i]);
		$values[$i] = "";
	    }
	    else {
		my $t = substr($values[$i], 0, $maxw);
		if ( substr($values[$i], $maxw, 1) eq " " ) {
		    push(@v, $ind.$t);
		    substr($values[$i], 0, length($t) + 1, "");
		}
		elsif ( $t =~ /^(.*)([ ]+)/ ) {
		    my $pre = $1;
		    push(@v, $ind.$pre);
		    substr($values[$i], 0, length($pre) + length($2), "");
		}
		else {
		    push(@v, $ind.$t);
		    substr($values[$i], 0, $maxw, "");
		}
		$more++;
	    }
	}
	my $t = sprintf($self->{format}, @v);
	$t =~ s/ +$//;
	push(@lines, $t) if $t =~ /\S/;
	last unless $more;
    }

    if ( $self->{lines} < @lines ) {
	$self->_needhdr(1);
	$self->_checkhdr;
    }
    $self->_print(@lines);

    # Post: Lines for cells.
    if ( $lineafter ) {
	$lineafter->{_style} = "";
	$self->add($lineafter);
    }
    # Post: Line for row.
    if ( $line_after ) {
	$self->_line;
    }
    # Post: Skip after this row.
    elsif ( $skip_after ) {
	$self->_skip;
    }
}

sub finish {
    my $self = shift;
    $self->_argcheck(0);
    $self->_checkskip(1);	# cancel skips.
    $self->SUPER::finish();
}

################ Pseudo-Internal (used by Base class) ################

sub _std_heading {
    my ($self) = @_;

    # Print column names.
    my $t = sprintf($self->{format},
		    map { $_->{title} }
		    grep {
			my $t = $self->_getstyle("_head", $_->{name});
			! $t->{ignore};
		    }
		    @{$self->_get_fields});

    # Add separator line.
    $t .= "-" x ($self->{width});
    $t .= "\n";

    # Remove trailing blanks.
    $t =~ s/ +$//gm;

    # Print it.
    $self->_print($t);

    $self->_needskip(0);

}

################ Internal methods ################

sub _print {
    my ($self, @values) = @_;
    my $value = join("", @values);
    $self->SUPER::_print($value);
    $self->{lines} -= ($value =~ tr/\n//);
}

sub _pageskip {
    my ($self) = @_;
    $self->{lines} = $self->{page};
}

sub _make_format {
    my ($self) = @_;

    my $width = 0;		# new width
    my $format = "";		# new format

    foreach my $a ( @{$self->_get_fields} ) {

	my $t = $self->_getstyle("_head", $a->{name});
	next if $t->{ignore};

	# Never mind the trailing blanks -- we'll trim anyway.
	$width += $a->{width} + 2;
	if ( $a->{align} eq "<" ) {
	    $format .= "%-".
	      join(".", ($a->{width}+2) x 2) .
		"s";
	}
	else {
	    $format .= "%".
	      join(".", ($a->{width}) x 2) .
		"s  ";
	}
    }

    # Store format and width in object.
    $self->{format} = $format . "\n";
    $self->{width}  = $width - 2;

    # PBP: Return nothing sensible.
    return;
}

sub _checkskip {
    my ($self, $cancel) = @_;
    return if !$self->_does_needskip || $self->{lines} <= 0;
    $self->_print("\n") unless $cancel;
    $self->_needskip(0);
}

sub _needskip {
    my $self = shift;
    $self->{needskip } = shift;
}
sub _does_needskip {
    my $self = shift;
    $self->{needskip};
}

sub _line {
    my ($self) = @_;

    $self->_checkhdr;
    $self->_checkskip(1);	# cancel skips.

    $self->_print("-" x ($self->{width}), "\n");
}

sub _skip {
    my ($self) = @_;

    $self->_checkhdr;
    $self->_needskip(1);
}

sub _center {
    my ($self, $text, $width) = @_;
    (" " x (($width - length($text))/2)) . $text;
}

sub _expand {
    my ($self, $text) = @_;
    $text =~ s/(.)/$1 /g;
    $text =~ s/ +$//;
    $text;
}

1;