File Coverage

blib/lib/Term/Table2.pm
Criterion Covered Total %
statement 290 290 100.0
branch 106 106 100.0
condition 86 86 100.0
subroutine 53 53 100.0
pod 3 3 100.0
total 538 538 100.0


line stmt bran cond sub pod time code
1             package Term::Table2;
2              
3 37     37   6865124 use v5.14;
  37         331  
4 37     37   181 use warnings FATAL => qw(all);
  37         67  
  37         1445  
5              
6 37     37   179 use List::Util qw(max min);
  37         69  
  37         2135  
7 37     37   15802 use Module::Load qw(load);
  37         35552  
  37         189  
8 37     37   18154 use Params::Validate qw(ARRAYREF CODEREF SCALAR validate);
  37         286445  
  37         2551  
9 37     37   267 use POSIX qw(floor ceil);
  37         66  
  37         270  
10 37     37   53897 use Term::ReadKey qw(GetTerminalSize);
  37         75  
  37         3425  
11              
12             use Class::XSAccessor
13             getters => {
14 37         99 map { $_ => $_; }
  481         1017  
15             qw(
16             broad_column
17             broad_header
18             broad_row
19             collapse
20             column_width
21             current_row
22             end_of_table
23             header
24             pad
25             page_height
26             rows
27             separate_rows
28             table_width
29             )
30 37     37   15272 };
  37         73356  
31              
32             sub _override_length { # Consider wide Unicode characters if possible i.e.
33 37     37   20900 no warnings qw(redefine once); # if Unicode::GCString can be used.
  37         69  
  37         1324  
34 37     37   17468 use subs qw(length);
  37         776  
  37         175  
35 39     39   5658 eval { load('Unicode::GCString') }; # Otherwise table content can be twisted
  39         130  
36 39 100   9   3178 *length = $@ ? sub { return CORE::length($_[0]) } : sub { return Unicode::GCString->new($_[0])->columns() };
  1         7  
  469         949  
37 39         993 return;
38             }
39              
40 37     37   4306 BEGIN { _override_length() }
41              
42             use constant { # Boolean values
43 37         2810 FALSE => 0,
44             TRUE => 1,
45 37     37   208 };
  37         65  
46             use constant { # Table flags
47 37         2863 ADJUST => 0,
48             CUT => 0,
49             SPLIT => 1,
50             WRAP => 2,
51 37     37   206 };
  37         64  
52             use constant { # Integer that hopefully can never be exceeded
53 37         5131 BIG_INT => ~0,
54 37     37   215 };
  37         74  
55             use constant { # Valid option combinations in form required by
56 37         12238 ALL_OPTIONS => { # Params::Validate::validate
57             header => {
58             default => [],
59             optional => 1,
60             },
61             rows => {
62             default => [],
63             optional => 1,
64             },
65             broad_column => {
66             default => [WRAP],
67             optional => 1,
68             },
69             broad_header => {
70             default => [WRAP],
71             optional => 1,
72             },
73             broad_row => {
74             default => WRAP,
75             optional => 1,
76             },
77             collapse => {
78             default => [FALSE],
79             optional => 1,
80             },
81             column_width => {
82             default => [ADJUST],
83             optional => 1,
84             },
85             pad => {
86             default => 1,
87             optional => 1,
88             },
89             page_height => {
90             default => \&_screen_height,
91             optional => 1,
92             },
93             separate_rows => {
94             default => FALSE,
95             optional => 1,
96             },
97             table_width => {
98             default => \&_screen_width,
99             optional => 1,
100             },
101             },
102 37     37   254 };
  37         85  
103             use constant {
104             OPTIONS_ARRAY => {
105 37         730 %{ALL_OPTIONS()},
106             rows => {
107             type => ARRAYREF,
108             callbacks => {
109             q('rows' element is an array reference) => \&_is_each_row_array,
110             q(all 'rows' elements have same length) => \&_are_all_rows_of_equal_length,
111             q('rows' elements contain defined scalars only) => \&_is_each_cell_scalar,
112             },
113             },
114             broad_row => {
115             type => SCALAR,
116             optional => 1,
117             callbacks => { q('broad_row' is either 'CUT', or 'SPLIT', or 'WRAP') => \&_is_cut_or_split_or_wrap },
118             },
119             collapse => {
120             type => ARRAYREF | SCALAR,
121             optional => 1,
122             },
123             column_width => {
124             type => ARRAYREF | SCALAR,
125             optional => 1,
126             callbacks => { q('column_width' is undefined or a positive integer) => \&_is_each_column_width_undef_or_int },
127             },
128             },
129             OPTIONS_CALLBACK => {
130 37         378 %{ALL_OPTIONS()},
131             broad_row => {
132             type => SCALAR,
133             optional => 1,
134             callbacks => { q('broad_row' is either 'CUT' or 'WRAP') => \&_is_cut_or_wrap },
135             },
136             column_width => {
137             type => ARRAYREF | SCALAR,
138             optional => 1,
139             callbacks => { q('column_width' is a positive integer) => \&_is_each_column_width_int },
140             },
141             },
142             OPTIONS_GENERAL => {
143 37         86 %{ALL_OPTIONS()},
  37         4042  
144             header => {
145             type => ARRAYREF,
146             optional => 1,
147             callbacks => { q('header' element is a scalar) => \&_is_scalar },
148             },
149             broad_column => {
150             type => ARRAYREF | SCALAR,
151             optional => 1,
152             callbacks => { q('broad_column' contains either 'CUT' or 'WRAP' only) => \&_is_each_column_flag_cut_or_wrap },
153             },
154             broad_header => {
155             type => ARRAYREF | SCALAR,
156             optional => 1,
157             callbacks => { q('broad_header' contains either 'CUT' or 'WRAP' only) => \&_is_each_column_flag_cut_or_wrap },
158             },
159             column_width => {
160             type => ARRAYREF | SCALAR,
161             optional => 1,
162             callbacks => { q('column_width' is a positive integer) => \&_is_each_column_width_int },
163             },
164             pad => {
165             type => ARRAYREF | SCALAR,
166             optional => 1,
167             callbacks => { q('pad' is undefined or a positive integer) => \&_is_undef_or_int },
168             },
169             page_height => {
170             type => SCALAR,
171             optional => 1,
172             callbacks => { q('page_height' is undefined or a positive integer) => \&_is_undef_or_int },
173             },
174             separate_rows => {
175             type => SCALAR,
176             optional => 1,
177             },
178             table_width => {
179             type => SCALAR,
180             optional => 1,
181             callbacks => { q('table_width' is undefined or a positive integer) => \&_is_undef_or_int },
182             },
183             },
184 37     37   250 };
  37         85  
185              
186 37     37   227 use Exporter qw(import);
  37         69  
  37         114686  
187             our @EXPORT_OK = qw(ADJUST CUT SPLIT WRAP);
188              
189             our $VERSION = '1.0.2';
190              
191             sub fetch { # Provides current line
192 210     210 1 7971 my ($self) = @_;
193              
194 210 100 100     330 return if $self->{'end_of_table'} && !@{$self->{':row_lines'}};
  3         19  
195              
196 208 100 100     531 $self->_get_next_lines() unless @{$self->{':row_lines'}} && $self->{':line_on_page'};
  208         515  
197 208         240 $self->{':line_on_page'}++;
198 208 100       294 $self->{':line_on_page'} = 0 if $self->{':line_on_page'} == $self->{':lines_per_page'};
199              
200 208         205 my $row = shift(@{$self->{':row_lines'}});
  208         269  
201 208 100       215 $self->{':row_buffer'} = [] unless @{$self->{':row_lines'}};
  208         352  
202              
203 208         506 return $_ = $row;
204             }
205              
206             sub fetch_all { # Provides all table lines at once
207 10     10 1 325 my ($self) = @_;
208              
209 10         16 my @lines;
210 10         36 push(@lines, $_) while $self->fetch();
211              
212 10         76 return \@lines;
213             }
214              
215             sub new { # Instantiate table object
216 11     11 1 8130 my ($class, @params) = @_;
217              
218 11         138 my $self = bless(
219             {
220             ':end_of_chunk' => FALSE, # End of vertical chunk in case of horizontal splitting
221             ':header_lines' => [], # One array element per header line
222             ':line_format' => '|', # Row line format
223             ':line_on_page' => 0, # Line number relative to the current page
224             ':lines_per_page' => 1, # Considers multiple lines per page depending on 'broad_row'
225             ':lines_per_row' => 1, # Considers multiple lines per row depending on 'broad_row'
226             ':number_of_columns' => undef, # Number of columns as supplied via 'rows' array
227             ':row_buffer' => [], # Current row content
228             ':row_lines' => [], # One array element per row line
229             ':separating_added' => FALSE, # Separating line is among ':row_lines'
230             ':separating_line' => '+', # Line separating table / header content
231             ':split_offset' => 0, # Horizontal offset from the table left side
232             ':total_width' => 0, # Table width independently of possible horizontal splitting
233             'current_row' => 0, # Order No. of current row
234             'end_of_table' => FALSE, # End of table in general (end of last chunk)
235             },
236             $class,
237             )->_validate(\@params);
238              
239 11 100       75 return ref($self->{'rows'}) eq 'ARRAY' ? $self->_set_defaults()->_init()
240             : $self->_copy_options(OPTIONS_CALLBACK, \@params);
241             }
242              
243             sub _are_all_rows_of_equal_length { # Checks if all rows have the same length as the 1st one
244 9     9   90 my ($rows) = @_;
245              
246 9         30 return !grep { @$_ != @{$rows->[0]} } @$rows;
  51         56  
  51         118  
247             }
248              
249             sub _copy_options { # Takes over values of required options into object
250 29     29   129 my ($self, $options, $params) = @_;
251              
252 29         76 my %params = @$params;
253              
254 29   100     96 $self //= {};
255 29         88 foreach my $option (keys(%$options)) {
256 301 100       480 $self->{$option} = $params{$option} if exists($params{$option});
257             }
258              
259 29         141 return $self;
260             }
261              
262             sub _cut_or_wrap_line {
263 164     164   304 my ($self, $line) = @_;
264              
265 164 100       261 $line = substr($line, $self->{':split_offset'}) if $self->{':split_offset'};
266              
267 164         199 my $line_too_long = length($line) > $self->{'table_width'};
268             # Wrap is required and line is long enough to be wrapped
269 164 100 100     2419 return unpack('(A' . $self->{'table_width'} . ')*', $line) if $self->{'broad_row'} == WRAP && $line_too_long;
270             # Wrap is not required and line is too long
271 127 100       210 return substr($line, 0, $self->{'table_width'}) if $line_too_long;
272              
273 105         198 return $line; # Line is too short for any change
274             }
275              
276             sub _extract_line { # Extract 1st remaining line from current table row
277 97     97   229 my ($self, $row, $broad_flags) = @_;
278              
279 97         113 my @line;
280              
281 97         204 foreach my $column_no (0 .. $self->{':number_of_columns'} - 1) {
282 289         339 my $column_width = $self->{'column_width'}[$column_no];
283 289         340 my $field = $row->[$column_no];
284              
285 289         276 $row->[$column_no] = do {
286 289 100       359 if (length($field) > $column_width) {
287 86         1096 push(@line, substr($field, 0, $column_width));
288 86 100       197 $broad_flags->[$column_no] == CUT ? '' : substr($field, $column_width);
289             }
290             else {
291 203         2376 push(@line, $field);
292 203         342 '';
293             }
294             };
295             }
296              
297 97         210 return \@line;
298             }
299              
300             sub _extract_lines { # Converts table row to array of output cell arrays
301 70     70   179 my ($self, $row, $broad_flags) = @_;
302              
303 70         137 my @row = @$row;
304              
305 70         80 my @lines;
306 70 100       130 if (@row) {
307 68         71 do {push(@lines, $self->_extract_line(\@row, $broad_flags))} while grep { $_ ne '' } @row;
  286         471  
  96         163  
308             }
309              
310 70         138 return \@lines;
311             }
312              
313             sub _get_next_lines { # Provides next lines from the current row
314 76     76   9642 my ($self) = @_;
315              
316 76 100 100     80 if (!@{$self->{':row_lines'}} && @{$self->_get_next_row()}) {
  76         168  
  69         109  
317 59         84 push(@{$self->{':row_lines'}},
318 59         67 map { $self->_cut_or_wrap_line($_) } @{$self->_prepare_row($self->{':row_buffer'}, $self->{'broad_column'})});
  84         151  
  59         104  
319 59         100 $self->{':separating_added'} = FALSE;
320             }
321              
322 21         38 unshift(@{$self->{':row_lines'}}, map { $self->_cut_or_wrap_line($_) } @{$self->{':header_lines'}})
  70         113  
  21         44  
323             if (ref($self->{'rows'}) eq 'ARRAY' && $self->{'current_row'} == 1 || !$self->{':line_on_page'})
324 76 100 100     373 && !$self->{'end_of_table'};
      100        
325              
326 76 100 100     236 if (($self->{':end_of_chunk'} # Ends up the table or separates two rows if required
      100        
327             || $self->{'separate_rows'} && $self->{':line_on_page'} + @{$self->{':row_lines'}} < $self->{'page_height'} - 1)
328             && !$self->{':separating_added'}) {
329 15         22 push(@{$self->{':row_lines'}}, $self->_cut_or_wrap_line($self->{':separating_line'}));
  15         32  
330 15         25 $self->{':separating_added'} = TRUE;
331             }
332              
333 76         99 return;
334             }
335              
336             sub _get_next_row { # Takes over next row
337 72     72   9354 my ($self) = @_;
338              
339 72 100 100     257 if ($self->{'end_of_table'} # End of table already reached or being reached just now
      100        
      100        
      100        
340             || ref($self->{'rows'}) eq 'ARRAY' && !$self->_get_next_row_from_array()
341             || ref($self->{'rows'}) eq 'CODE' && !$self->_get_next_row_from_callback()) {
342 12         40 $self->{':row_buffer'} = [];
343             }
344             else {
345 60         89 $self->{'current_row'}++;
346             }
347              
348 72         175 return $self->{':row_buffer'};
349             }
350              
351             sub _get_next_row_from_array { # Takes over next row from array
352 69     69   184 my ($self) = @_;
353              
354 69         86 my $current_row = $self->{'current_row'};
355              
356 69         71 $self->{':end_of_chunk'} = $current_row == $#{$self->{'rows'}};
  69         123  
357              
358 69 100       82 if ($current_row > $#{$self->{'rows'}}) {
  69         120  
359 12 100 100     46 if ($self->{'broad_row'} != SPLIT || $self->{':split_offset'} + $self->{'table_width'} >= $self->{':total_width'}) {
360 10         16 $self->{'end_of_table'} = TRUE;
361 10         56 return FALSE;
362             }
363              
364 2         5 $self->{'current_row'} = $current_row = 0;
365 2         4 $self->{':split_offset'} += $self->{'table_width'};
366             }
367              
368 59         96 $self->{':row_buffer'} = $self->{'rows'}[$current_row];
369 59         66 $self->{'end_of_table'} = FALSE;
370              
371 59         256 return TRUE;
372             }
373              
374             sub _get_next_row_from_callback { # Takes over next row delivered by callback function
375 8     8   8341 my ($self) = @_;
376              
377 8         10 my $row = &{$self->{'rows'}};
  8         19  
378              
379 8 100       38 unless (defined($self->{':number_of_columns'})) {
380 3 100       13 $self->{':number_of_columns'} = ref($row) eq 'ARRAY' ? @$row : 0;
381 3 100       19 die("Row $self->{'current_row'}: not an array reference") if ref($row) ne 'ARRAY';
382 2         7 $self->_validate_for_callback()->_set_defaults()->_init();
383             }
384              
385 7 100       20 unless (defined($row)) {
386 2         5 $self->{':end_of_chunk'} = $self->{'end_of_table'} = TRUE;
387 2         8 return FALSE;
388             }
389              
390 5         9 my $number_of_columns = $self->{':number_of_columns'};
391              
392 5 100       29 die("Row $self->{'current_row'}: not an array reference") if ref($row) ne 'ARRAY';
393 4 100       21 die("Row $self->{'current_row'}: wrong number of elements (", scalar(@$row), " instead of $number_of_columns)")
394             if scalar(@$row) != $number_of_columns;
395 3         9 foreach (0 .. $number_of_columns - 1) {
396 5 100       23 die("Row $self->{'current_row'}: element No. $_ is ", ref($row->[$_]), ' not a scalar') if ref($row->[$_]);
397             }
398              
399 2         7 $self->{':row_buffer'} = _strip_trailing_blanks($row);
400              
401 2         12 return TRUE;
402             }
403              
404             sub _init { # Set remaining attributes during object instantiating
405 13     13   9569 my ($self) = @_;
406              
407 13   100     48 $self->{'page_height'} ||= BIG_INT;
408 13   100     41 $self->{'table_width'} ||= BIG_INT;
409              
410 13 100       38 $self->{'pad'} = [($self->{'pad'}) x max(scalar(@{$self->{'column_width'}}), 1)] unless ref($self->{'pad'});
  10         84  
411              
412             die(
413             "Table width ($self->{'table_width'}) is lower than the width of the narrowest possible column i.e. 1 character, ",
414 1         19 'left-side column separator, and the lowest left-side padding (', min(@{$self->{'pad'}}), ')'
415 13 100       37 ) if $self->{'table_width'} < 1 + 1 + min(@{$self->{'pad'}});
  13         68  
416              
417             $self->{'column_width'} = [map { $self->{'column_width'}[$_] == ADJUST ? $self->_max_column_width($_)
418 33 100       90 : $self->{'column_width'}[$_] }
419 12         34 0 .. $#{$self->{'column_width'}}];
  12         32  
420              
421 12         45 $self->_set_line_format();
422 12         44 $self->{':header_lines'} = $self->_prepare_row($self->{'header'}, $self->{'broad_header'});
423 12 100       28 if (@{$self->{':header_lines'}}) {
  12         34  
424 10         13 unshift(@{$self->{':header_lines'}}, $self->{':separating_line'});
  10         31  
425 10         22 push (@{$self->{':header_lines'}}, $self->{':separating_line'});
  10         24  
426             }
427              
428             $self->{':lines_per_row'} = $self->{'broad_row'} == WRAP
429 12 100       47 ? ceil(length($self->{':separating_line'}) / $self->{'table_width'})
430             : 1;
431 12         250 my $header_height = @{$self->{':header_lines'}} * $self->{':lines_per_row'};
  12         33  
432             $self->{':lines_per_page'} = min(BIG_INT,
433             $header_height
434             + floor(($self->{'page_height'} - $header_height) / $self->{':lines_per_row'})
435 12         97 * $self->{':lines_per_row'});
436              
437 12 100       18 if (@{$self->{':header_lines'}}) { # At least one row or one separating line under the header
  12         31  
438 10         19 my $page_height = $header_height + $self->{':lines_per_row'};
439             die("Page height ($self->{'page_height'}) is lower than the minimum possible page height ($page_height)")
440 10 100       41 if $self->{'page_height'} < $page_height;
441             }
442              
443 11         23 $self->{'current_row'} = 0;
444              
445 11         55 return $self;
446             }
447              
448             sub _is_cut_or_split_or_wrap { # Check if each column flag is CUT, or SPLIT, or WRAP
449 6     6   7096 my ($flag) = @_;
450              
451 6 100       13 return FALSE unless _is_int($flag); # This split-up in 2 "returns" is only necessary due to
452 5   100     61 return $flag == CUT || $flag == SPLIT || $flag == WRAP; # a weakness of Devel::Cover
453             }
454              
455             sub _is_cut_or_wrap { # Check if flag is CUT or WRAP
456 7     7   6788 my ($flag) = @_;
457              
458 7 100       15 return FALSE unless _is_int($flag); # This split-up in 2 "returns" is only necessary due to
459 6   100     41 return $flag == CUT || $flag == WRAP; # a weakness of Devel::Cover
460             }
461              
462             sub _is_each_cell_scalar { # Check if each cell in each row is a defined scalar
463 10     10   89 my ($rows) = @_;
464              
465 10         25 return !grep { !_is_scalar($_) } @$rows;
  53         77  
466             }
467              
468             sub _is_each_column_flag_cut_or_wrap { # Check if each column flag is CUT or WRAP
469 2     2   73 my ($flag) = @_;
470              
471 2 100       8 return ref($flag) ? !grep { !_is_cut_or_wrap($_) } @$flag : _is_cut_or_wrap($flag);
  2         6  
472             }
473              
474             sub _is_each_column_width_int { # Check if each column width is positive integer
475 12     12   6612 my ($width) = @_;
476              
477 12 100 100     60 return ref($width) ? !grep { !$_ || !_is_int($_) } @$width : $width && _is_int($width);
  3   100     9  
478             }
479              
480             sub _is_each_column_width_undef_or_int { # Check if each column width is udefined or positive integer
481 10     10   96 my ($width) = @_;
482              
483 10 100       42 return ref($width) ? !grep { !_is_undef_or_int($_) } @$width : _is_undef_or_int($width);
  3         8  
484             }
485              
486             sub _is_each_row_array { # Check if each row is an array
487 9     9   92 my ($rows) = @_;
488              
489 9         20 return !grep { ref ne 'ARRAY' } @$rows;
  51         117  
490             }
491              
492             sub _is_int { # Check if defined value is an integer
493 46     46   163 my ($value) = @_;
494              
495 46   100     514 return !ref($value) && $value =~ /^\d+$/;
496             }
497              
498             sub _is_scalar { # Check if each cell in a row is a defined scalar
499 62     62   149 my ($value) = @_;
500              
501 62 100       79 return !grep { !defined || ref } @$value;
  183         538  
502             }
503              
504             sub _is_undef_or_int { # Check if value is defined or an integer
505 31     31   777 my ($value) = @_;
506              
507 31   100     105 return !defined($value) || _is_int($value);
508             }
509              
510             sub _max_column_width { # Estimates maximum length of text in column
511 2     2   6423 my ($self, $column_no) = @_;
512              
513 2 100       3 my $width = @{$self->{'header'}} ? length($self->{'header'}[$column_no]) : 0;
  2         11  
514              
515 2         52 return max($width, map { length($_->[$column_no]) } @{$self->{'rows'}});
  4         59  
  2         5  
516             }
517              
518             sub _prepare_row { # Converts table row to array of output strings
519 69     69   411 my ($self, $row, $broad_flags) = @_;
520             # Ignore possible redundant columns in header
521 69 100       136 @$row = @$row[0 .. $self->{':number_of_columns'} - 1] if $#$row >= $self->{':number_of_columns'};
522              
523 69         92 return [map { sprintf($self->{':line_format'}, @$_) } @{$self->_extract_lines($row, $broad_flags)}];
  98         441  
  69         121  
524             }
525              
526 1     1   4 sub _screen_height { return (GetTerminalSize())[1] }
527              
528 1     1   4 sub _screen_width { return (GetTerminalSize())[0] }
529              
530             sub _set_defaults { # Set default attributes if they are omitted
531 11     11   393 my ($self) = @_;
532              
533 11         21 for my $option (keys(%{ALL_OPTIONS()})) {
  11         38  
534 121 100       257 if (ref(ALL_OPTIONS->{$option}{'default'}) eq 'ARRAY') {
535             my $default = defined($self->{$option}) && !ref($self->{$option}) ? $self->{$option}
536 66 100 100     228 : ALL_OPTIONS->{$option}{'default'}[0];
537 66 100       136 $self->{$option} = [] unless ref($self->{$option});
538 66 100 100     186 next if $option eq 'header' || $option eq 'rows';
539 44   100     254 $self->{$option}[$_] //= $default foreach 0 .. $self->{':number_of_columns'} - 1;
540             }
541             else {
542 55         77 my $default = ALL_OPTIONS->{$option}{'default'};
543 55 100       139 $self->{$option} = ref($default) eq 'CODE' ? &$default() : $default unless defined($self->{$option});
    100          
544             }
545             }
546              
547 11         65 return $self;
548             }
549              
550             sub _set_line_format {
551 12     12   162 my ($self) = @_;
552              
553 12         21 my $table_width = 1;
554              
555 12         34 foreach my $column_no (0 .. $self->{':number_of_columns'} - 1) {
556 32         43 my $column_width = $self->{'column_width'}[$column_no];
557 32 100 100     77 if ($self->{'collapse'}[$column_no] && !$column_width) {
558 2         5 $self->{':line_format'} .= '%s';
559             }
560             else {
561 30         37 my $pad = $self->{'pad'}[$column_no];
562 30         79 $self->{':line_format'} .= ' ' x $pad . '%-' . $column_width . 's' . ' ' x $pad . '|';
563 30         56 $self->{':separating_line'} .= '-' x ($pad + $column_width + $pad) . '+';
564 30         41 $table_width += $pad + $column_width + $pad + 1;
565             }
566              
567 32 100 100     77 last if $self->{'broad_row'} == CUT && $table_width >= $self->{'table_width'};
568             }
569              
570 12         19 $self->{':total_width'} = $table_width;
571 12 100       39 $self->{'current_row'} = 0 if $table_width == 1; # This table has no content
572              
573 12         29 return $self;
574             }
575              
576             sub _strip_trailing_blanks { # Strips down trailing blanks from all cell values in row
577 51     51   141 my ($row) = @_;
578              
579 51         69 return [map { s/\s+$//r } @$row];
  154         332  
580             }
581              
582             sub _validate {
583 12     12   1068 my ($self, $params) = @_;
584              
585 12         27 validate(@$params, {%{ALL_OPTIONS()}, rows => {'optional' => TRUE, 'type' => ARRAYREF | CODEREF}});
  12         373  
586              
587 12         80 my %params = @$params;
588 12   100     91 $self->{'rows'} = $params{'rows'} // [];
589 12 100       67 return ref($params{'rows'}) eq 'ARRAY' ? $self->_validate_for_array([%{_copy_options(undef, OPTIONS_ARRAY, $params)}])
  9         30  
590             : $self;
591             }
592              
593             sub _validate_for_array {
594 10     10   6102 my ($self, $params) = @_;
595              
596 10         16 $self->{'rows'} = [map { _strip_trailing_blanks($_) } @{$self->{'rows'}}];
  51         79  
  10         27  
597 10 100       22 $self->{':number_of_columns'} = @{$self->{'rows'}} ? @{$self->{'rows'}[0]} : 0;
  10         39  
  8         18  
598              
599 10         120 validate(@$params, OPTIONS_ARRAY);
600              
601 10         54 return $self->_copy_options(OPTIONS_ARRAY, $params)->_validate_general($params);
602             }
603              
604             sub _validate_for_callback {
605 2     2   414 my ($self, $params) = @_;
606              
607 2         15 validate(@$params, OPTIONS_CALLBACK);
608              
609 2         21 return $self->_copy_options(OPTIONS_CALLBACK, $params)->_validate_general($params);
610             }
611              
612             sub _validate_general {
613 13     13   7874 my ($self, $params) = @_;
614              
615 13         114 validate(@$params, OPTIONS_GENERAL);
616              
617 13         59 my %params = @$params;
618             die("The 'header' parameter contains less elements than an element of the 'rows' parameter")
619 13 100 100     69 if exists($params{'header'}) && exists($params{'rows'}) && @{$params{'header'}} < $self->{':number_of_columns'};
  10   100     48  
620              
621 12         29 return $self->_copy_options(OPTIONS_GENERAL, $params);
622             }
623              
624             1;