File Coverage

lib/Template/Plugin/Table.pm
Criterion Covered Total %
statement 70 78 89.7
branch 32 46 69.5
condition 6 13 46.1
subroutine 10 10 100.0
pod 1 5 20.0
total 119 152 78.2


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   10 use strict;
  3         4  
  3         77  
23 3     3   10 use warnings;
  3         4  
  3         115  
24 3     3   11 use base 'Template::Plugin';
  3         4  
  3         854  
25 3     3   11 use Scalar::Util 'blessed';
  3         4  
  3         2055  
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 16 my ($class, $context, $data, $params) = @_;
44 16         16 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     44 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       34 return $class->error('invalid table data, expecting a list')
57             unless ref $data eq 'ARRAY';
58              
59 16   50     24 $params ||= { };
60 16 50       25 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         39 @$params{ map { uc } keys %$params } = values %$params;
  22         56  
65              
66 16         21 $size = scalar @$data;
67 16   100     69 $overlap = $params->{ OVERLAP } || 0;
68              
69             # calculate number of columns based on a specified number of rows
70 16 100       26 if ($rows = $params->{ ROWS }) {
    50          
71 14 100       20 if ($size < $rows) {
72 1         2 $rows = $size; # pad?
73 1         1 $cols = 1;
74 1         2 $coloff = 0;
75             }
76             else {
77 13         13 $coloff = $rows - $overlap;
78 13 100       40 $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       4 if ($size < $cols) {
85 1         2 $cols = $size;
86 1         1 $rows = 1;
87 1         2 $coloff = 1;
88             }
89             else {
90 1 50       4 $coloff = int ($size / $cols)
91             + ($size % $cols > $overlap ? 1 : 0);
92 1         1 $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       109 _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 26 my ($self, $row) = @_;
123             my ($data, $cols, $offset, $size, $pad)
124 10         26 = @$self{ qw( _DATA _NCOLS _COLOFF _SIZE _PAD) };
125 10         12 my @set;
126              
127             # return all rows if row number not specified
128 10 50       17 return $self->rows()
129             unless defined $row;
130              
131 10 50 33     46 return () if $row >= $self->{ _NROWS } || $row < 0;
132            
133 10         12 my $index = $row;
134              
135 10         22 for (my $c = 0; $c < $cols; $c++) {
136 35 50       50 push(@set, $index < $size
    100          
137             ? $data->[$index]
138             : ($pad ? undef : ()));
139 35         52 $index += $offset;
140             }
141 10         71 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 38 my ($self, $col) = @_;
155 34         41 my ($data, $size) = @$self{ qw( _DATA _SIZE ) };
156 34         22 my ($start, $end);
157 34         22 my $blanks = 0;
158              
159             # return all cols if row number not specified
160 34 100       47 return $self->cols()
161             unless defined $col;
162              
163 32 50 33     98 return () if $col >= $self->{ _NCOLS } || $col < 0;
164              
165 32         22 $start = $self->{ _COLOFF } * $col;
166 32         29 $end = $start + $self->{ _NROWS } - 1;
167 32 100       34 $end = $start if $end < $start;
168 32 100       38 if ($end >= $size) {
169 6         5 $blanks = ($end - $size) + 1;
170 6         4 $end = $size - 1;
171             }
172 32 100       39 return () if $start >= $size;
173             return [ @$data[$start..$end],
174 31 100       140 $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         3 return [ map { $self->row($_) } (0..$self->{ _NROWS }-1) ];
  1         3  
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 6 my $self = shift;
198 6         12 return [ map { $self->col($_) } (0..$self->{ _NCOLS }-1) ];
  30         36  
199             }
200              
201              
202             #------------------------------------------------------------------------
203             # AUTOLOAD
204             #
205             # Provides read access to various internal data members.
206             #------------------------------------------------------------------------
207              
208             sub AUTOLOAD {
209 16     16   11 my $self = shift;
210 16         21 my $item = $AUTOLOAD;
211 16         58 $item =~ s/.*:://;
212 16 50       95 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__