# 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;