File Coverage

blib/lib/Data/TableReader/Decoder/Spreadsheet.pm
Criterion Covered Total %
statement 67 85 78.8
branch 15 34 44.1
condition 11 23 47.8
subroutine 14 16 87.5
pod 1 1 100.0
total 108 159 67.9


line stmt bran cond sub pod time code
1             package Data::TableReader::Decoder::Spreadsheet;
2 1     1   695 use Moo 2;
  1         17  
  1         7  
3 1     1   414 use Carp 'croak';
  1         3  
  1         69  
4 1     1   660 use IO::Handle;
  1         7709  
  1         809  
5              
6             extends 'Data::TableReader::Decoder';
7              
8             # ABSTRACT: Base class for implementing spreadsheet decoders
9             our $VERSION = '0.021'; # VERSION
10              
11              
12             has workbook => ( is => 'lazy' );
13             has sheet => ( is => 'ro' );
14             has xls_formatter => ( is => 'rw' );
15              
16             # Arrayref of all sheets we can search
17             has _sheets => ( is => 'lazy' );
18              
19             sub _build__sheets {
20 2     2   21 my $self= shift;
21              
22             # If we have ->sheet and it is a worksheet object, then no need to do anything else
23 2 50 66     26 if ($self->sheet && ref($self->sheet) && ref($self->sheet)->can('get_cell')) {
      66        
24 1         5 return [ $self->sheet ];
25             }
26              
27             # Else we need to scan sheets from the excel file. Make sure we have the file
28 1         17 my @sheets= $self->workbook->worksheets;
29 1 50       11 @sheets or croak "No worksheets in file?";
30 1 50       6 if (defined $self->sheet) {
31 0 0       0 if (ref($self->sheet) eq 'Regexp') {
    0          
    0          
32 0         0 @sheets= grep { $_->get_name =~ $self->sheet } @sheets;
  0         0  
33             } elsif (ref($self->sheet) eq 'CODE') {
34 0         0 @sheets= grep { $self->sheet->($_) } @sheets;
  0         0  
35             } elsif (!ref $self->sheet) {
36 0         0 @sheets= grep { $_->get_name eq $self->sheet } @sheets;
  0         0  
37             } else {
38 0         0 croak "Unknown type of sheet specification: ".$self->sheet;
39             }
40             }
41              
42 1         6 return \@sheets;
43             }
44              
45             sub _oo_rowmax_fix { # openoffice saves bogus rowmax, try and fix
46 7     7   13 my ($s, $rowmax)= @_;
47             my $final_row_max= ($s and ref $s->{Cells} eq "ARRAY" and $#{$s->{Cells}} < $rowmax) #
48 7 50 33     44 ? $#{$s->{Cells}} : $rowmax;
  0         0  
49 7         13 return $final_row_max;
50             }
51              
52             sub iterator {
53 4     4 1 7870 my $self= shift;
54 4         117 my $sheets= $self->_sheets;
55 4         20 my $sheet= $sheets->[0];
56 4 50       21 my ($colmin, $colmax)= $sheet? $sheet->col_range() : (0,-1);
57 4 50       51 my ($rowmin, $rowmax)= $sheet? $sheet->row_range() : (0,-1);
58 4         34 $rowmax= _oo_rowmax_fix $sheet, $rowmax;
59 4         42 my $row= $rowmin-1;
60             Data::TableReader::Decoder::Spreadsheet::_Iter->new(
61             sub {
62 16     16   32 my $slice= shift;
63 16 100       63 return undef unless $row < $rowmax;
64 13         23 ++$row;
65 13         20 my $x;
66 13 50       30 if ($slice) {
67             return [ map {
68 0   0     0 $x= ($x= $sheet->get_cell($row, $_)) && $x->value;
  0         0  
69 0 0       0 defined $x? $x : ''
70             } @$slice ];
71             } else {
72             return [ map {
73 13   100     38 $x= ($x= $sheet->get_cell($row, $_)) && $x->value;
  71         149  
74 71 100       1060 defined $x? $x : ''
75             } 0 .. $colmax ];
76             }
77             },
78             {
79 4         80 sheets => $sheets,
80             sheet_idx => 0,
81             sheet_ref => \$sheet,
82             row_ref => \$row,
83             colmax_ref => \$colmax,
84             rowmax_ref => \$rowmax,
85             origin => [ $sheet, $row ],
86             }
87             );
88             }
89              
90             # If you need to subclass this iterator, don't. Just implement your own.
91             # i.e. I'm not declaring this implementation stable, yet.
92 1     1   604 use Data::TableReader::Iterator;
  1         4  
  1         48  
93 1     1   564 BEGIN { @Data::TableReader::Decoder::Spreadsheet::_Iter::ISA= ('Data::TableReader::Iterator'); }
94              
95             sub Data::TableReader::Decoder::Spreadsheet::_Iter::position {
96 0     0   0 my $f= shift->_fields;
97             ($f->{sheet_idx} > 0? 'sheet '.($f->{sheet_idx}+1).' ' : '')
98 0 0       0 .'row '.(1+${ $f->{row_ref} });
  0         0  
99             }
100              
101             sub Data::TableReader::Decoder::Spreadsheet::_Iter::row {
102 6     6   15 1 + ${ shift->_fields->{row_ref} };
  6         24  
103             }
104              
105             sub Data::TableReader::Decoder::Spreadsheet::_Iter::dataset_idx {
106 6     6   23 shift->_fields->{sheet_idx};
107             }
108              
109             sub Data::TableReader::Decoder::Spreadsheet::_Iter::progress {
110 0     0   0 my $f= shift->_fields;
111 0   0     0 return ${ $f->{row_ref} } / (${ $f->{rowmax_ref} } || 1);
  0         0  
112             }
113              
114             sub Data::TableReader::Decoder::Spreadsheet::_Iter::tell {
115 2     2   10 my $f= shift->_fields;
116 2         6 return [ $f->{sheet_idx}, ${$f->{row_ref}} ];
  2         10  
117             }
118              
119             sub Data::TableReader::Decoder::Spreadsheet::_Iter::seek {
120 3     3   8 my ($self, $to)= @_;
121 3         9 my $f= $self->_fields;
122 3   33     10 $to ||= $f->{origin};
123 3         6 my ($sheet_idx, $row)= @$to;
124 3         6 my $sheet= $f->{sheets}[$sheet_idx];
125 3 50       16 my ($colmin, $colmax)= $sheet? $sheet->col_range() : (0,-1);
126 3 50       33 my ($rowmin, $rowmax)= $sheet? $sheet->row_range() : (0,-1);
127 3         25 $rowmax= _oo_rowmax_fix $sheet, $rowmax;
128 3 100       7 $row= $rowmin-1 unless defined $row;
129 3         6 $f->{sheet_idx}= $sheet_idx;
130 3         6 ${$f->{sheet_ref}}= $sheet;
  3         5  
131 3         5 ${$f->{row_ref}}= $row;
  3         7  
132 3         4 ${$f->{colmax_ref}}= $colmax;
  3         5  
133 3         3 ${$f->{rowmax_ref}}= $rowmax;
  3         6  
134 3         16 1;
135             }
136              
137             sub Data::TableReader::Decoder::Spreadsheet::_Iter::next_dataset {
138 3     3   8 my $self= shift;
139 3         12 my $f= $self->_fields;
140             return defined $f->{sheets}[ $f->{sheet_idx}+1 ]
141 3   66     22 && $self->seek([ $f->{sheet_idx}+1 ]);
142             }
143              
144             1;
145              
146             __END__