File Coverage

lib/Template/Plugin/Table.pm
Criterion Covered Total %
statement 66 78 84.6
branch 31 46 67.3
condition 6 13 46.1
subroutine 9 10 90.0
pod 1 5 20.0
total 113 152 74.3


line stmt bran cond sub pod time code
1             #============================================================= -*-Perl-*-
2             #
3             # Template::Plugin::Table
4             #
5             # DESCRIPTION
6             # Plugin to order a linear data set into a virtual 2-dimensional table
7             # from which row and column permutations can be fetched.
8             #
9             # AUTHOR
10             # Andy Wardley
11             #
12             # COPYRIGHT
13             # Copyright (C) 2000-2007 Andy Wardley. All Rights Reserved.
14             #
15             # This module is free software; you can redistribute it and/or
16             # modify it under the same terms as Perl itself.
17             #
18             #============================================================================
19              
20             package Template::Plugin::Table;
21              
22 3     3   16 use strict;
  3         8  
  3         132  
23 3     3   16 use warnings;
  3         15  
  3         114  
24 3     3   16 use base 'Template::Plugin';
  3         4  
  3         1538  
25 3     3   20 use Scalar::Util 'blessed';
  3         7  
  3         3378  
26              
27             our $VERSION = 2.71;
28             our $AUTOLOAD;
29              
30              
31             #------------------------------------------------------------------------
32             # new($context, \@data, \%args)
33             #
34             # This constructor method initialises the object to iterate through
35             # the data set passed by reference to a list as the first parameter.
36             # It calculates the shape of the permutation table based on the ROWS
37             # or COLS parameters specified in the $args hash reference. The
38             # OVERLAP parameter may be provided to specify the number of common
39             # items that should be shared between subsequent columns.
40             #------------------------------------------------------------------------
41              
42             sub new {
43 16     16 1 35 my ($class, $context, $data, $params) = @_;
44 16         28 my ($size, $rows, $cols, $coloff, $overlap, $error);
45              
46             # if the data item is a reference to a Template::Iterator object,
47             # or subclass thereof, we call its get_all() method to extract all
48             # the data it contains
49 16 50 33     77 if (blessed($data) && $data->isa('Template::Iterator')) {
50 0         0 ($data, $error) = $data->get_all();
51 0 0       0 return $class->error("iterator failed to provide data for table: ",
52             $error)
53             if $error;
54             }
55            
56 16 50       82 return $class->error('invalid table data, expecting a list')
57             unless ref $data eq 'ARRAY';
58              
59 16   50     42 $params ||= { };
60 16 50       45 return $class->error('invalid table parameters, expecting a hash')
61             unless ref $params eq 'HASH';
62              
63             # ensure keys are folded to upper case
64 16         64 @$params{ map { uc } keys %$params } = values %$params;
  22         91  
65              
66 16         42 $size = scalar @$data;
67 16   100     124 $overlap = $params->{ OVERLAP } || 0;
68              
69             # calculate number of columns based on a specified number of rows
70 16 100       58 if ($rows = $params->{ ROWS }) {
    50          
71 14 100       78 if ($size < $rows) {
72 1         3 $rows = $size; # pad?
73 1         3 $cols = 1;
74 1         2 $coloff = 0;
75             }
76             else {
77 13         29 $coloff = $rows - $overlap;
78 13 100       81 $cols = int ($size / $coloff)
79             + ($size % $coloff > $overlap ? 1 : 0)
80             }
81             }
82             # calculate number of rows based on a specified number of columns
83             elsif ($cols = $params->{ COLS }) {
84 2 100       9 if ($size < $cols) {
85 1         2 $cols = $size;
86 1         3 $rows = 1;
87 1         3 $coloff = 1;
88             }
89             else {
90 1 50       8 $coloff = int ($size / $cols)
91             + ($size % $cols > $overlap ? 1 : 0);
92 1         2 $rows = $coloff + $overlap;
93             }
94             }
95             else {
96 0         0 $rows = $size;
97 0         0 $cols = 1;
98 0         0 $coloff = 0;
99             }
100            
101             bless {
102             _DATA => $data,
103             _SIZE => $size,
104             _NROWS => $rows,
105             _NCOLS => $cols,
106             _COLOFF => $coloff,
107             _OVERLAP => $overlap,
108 16 100       240 _PAD => defined $params->{ PAD } ? $params->{ PAD } : 1,
109             }, $class;
110             }
111              
112              
113             #------------------------------------------------------------------------
114             # row($n)
115             #
116             # Returns a reference to a list containing the items in the row whose
117             # number is specified by parameter. If the row number is undefined,
118             # it calls rows() to return a list of all rows.
119             #------------------------------------------------------------------------
120              
121             sub row {
122 10     10 0 59 my ($self, $row) = @_;
123 10         44 my ($data, $cols, $offset, $size, $pad)
124             = @$self{ qw( _DATA _NCOLS _COLOFF _SIZE _PAD) };
125 10         15 my @set;
126              
127             # return all rows if row number not specified
128 10 50       46 return $self->rows()
129             unless defined $row;
130              
131 10 50 33     66 return () if $row >= $self->{ _NROWS } || $row < 0;
132            
133 10         19 my $index = $row;
134              
135 10         33 for (my $c = 0; $c < $cols; $c++) {
136 35 50       82 push(@set, $index < $size
    100          
137             ? $data->[$index]
138             : ($pad ? undef : ()));
139 35         85 $index += $offset;
140             }
141 10         109 return \@set;
142             }
143              
144              
145             #------------------------------------------------------------------------
146             # col($n)
147             #
148             # Returns a reference to a list containing the items in the column whose
149             # number is specified by parameter. If the column number is undefined,
150             # it calls cols() to return a list of all columns.
151             #------------------------------------------------------------------------
152              
153             sub col {
154 34     34 0 94 my ($self, $col) = @_;
155 34         69 my ($data, $size) = @$self{ qw( _DATA _SIZE ) };
156 34         47 my ($start, $end);
157 34         37 my $blanks = 0;
158              
159             # return all cols if row number not specified
160 34 100       82 return $self->cols()
161             unless defined $col;
162              
163 32 50 33     146 return () if $col >= $self->{ _NCOLS } || $col < 0;
164              
165 32         58 $start = $self->{ _COLOFF } * $col;
166 32         48 $end = $start + $self->{ _NROWS } - 1;
167 32 100       67 $end = $start if $end < $start;
168 32 100       62 if ($end >= $size) {
169 6         10 $blanks = ($end - $size) + 1;
170 6         10 $end = $size - 1;
171             }
172 32 100       69 return () if $start >= $size;
173             return [ @$data[$start..$end],
174 31 100       343 $self->{ _PAD } ? ((undef) x $blanks) : () ];
175             }
176              
177              
178             #------------------------------------------------------------------------
179             # rows()
180             #
181             # Returns all rows as a reference to a list of rows.
182             #------------------------------------------------------------------------
183              
184             sub rows {
185 1     1 0 3 my $self = shift;
186 1         5 return [ map { $self->row($_) } (0..$self->{ _NROWS }-1) ];
  1         5  
187             }
188              
189              
190             #------------------------------------------------------------------------
191             # cols()
192             #
193             # Returns all rows as a reference to a list of rows.
194             #------------------------------------------------------------------------
195              
196             sub cols {
197 6     6 0 13 my $self = shift;
198 6         21 return [ map { $self->col($_) } (0..$self->{ _NCOLS }-1) ];
  30         74  
199             }
200              
201              
202             #------------------------------------------------------------------------
203             # AUTOLOAD
204             #
205             # Provides read access to various internal data members.
206             #------------------------------------------------------------------------
207              
208             sub AUTOLOAD {
209 0     0     my $self = shift;
210 0           my $item = $AUTOLOAD;
211 0           $item =~ s/.*:://;
212 0 0         return if $item eq 'DESTROY';
213              
214 0 0         if ($item =~ /^(?:data|size|nrows|ncols|overlap|pad)$/) {
215 0           return $self->{ $item };
216             }
217             else {
218 0           return (undef, "no such table method: $item");
219             }
220             }
221              
222              
223              
224             1;
225              
226             __END__