File Coverage

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


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