|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Data::TableReader::Decoder::Spreadsheet;  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $Data::TableReader::Decoder::Spreadsheet::VERSION = '0.009';  | 
| 
3
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
455
 | 
 use Moo 2;  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
4
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
248
 | 
 use Carp 'croak';  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
    | 
| 
5
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
436
 | 
 use IO::Handle;  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5000
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
529
 | 
    | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 extends 'Data::TableReader::Decoder';  | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ABSTRACT: Base class for implementing spreadsheet decoders  | 
| 
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
  
 | 
 
 | 
23
 | 
 	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
  
 | 
 
 | 
 
 | 
23
 | 
 	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
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
 	my @sheets= $self->workbook->worksheets;  | 
| 
29
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 	@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
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	return \@sheets;  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _oo_rowmax_fix {    # openoffice saves bogus rowmax, try and fix  | 
| 
46
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
8
 | 
     my ($s, $rowmax)= @_;  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $final_row_max= ($s and ref $s->{Cells} eq "ARRAY" and $#{$s->{Cells}} < $rowmax)    #  | 
| 
48
 | 
6
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
31
 | 
       ? $#{$s->{Cells}} : $rowmax;  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
49
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
     return $final_row_max;  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub iterator {  | 
| 
53
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
1
  
 | 
55386
 | 
 	my $self= shift;  | 
| 
54
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
89
 | 
 	my $sheets= $self->_sheets;  | 
| 
55
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
 	my $sheet= $sheets->[0];  | 
| 
56
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 	my ($colmin, $colmax)= $sheet? $sheet->col_range() : (0,-1);  | 
| 
57
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
37
 | 
 	my ($rowmin, $rowmax)= $sheet? $sheet->row_range() : (0,-1);  | 
| 
58
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
 	$rowmax= _oo_rowmax_fix $sheet, $rowmax;  | 
| 
59
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 	my $row= $rowmin-1;  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	Data::TableReader::Decoder::Spreadsheet::_Iter->new(  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		sub {  | 
| 
62
 | 
16
 | 
 
 | 
 
 | 
  
16
  
 | 
 
 | 
30
 | 
 			my $slice= shift;  | 
| 
63
 | 
16
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
40
 | 
 			return undef unless $row < $rowmax;  | 
| 
64
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
 			++$row;  | 
| 
65
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 			my $x;  | 
| 
66
 | 
13
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
27
 | 
 			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
  
 | 
 
 | 
 
 | 
25
 | 
 					$x= ($x= $sheet->get_cell($row, $_)) && $x->value;  | 
| 
 
 | 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
136
 | 
    | 
| 
74
 | 
71
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
854
 | 
 					defined $x? $x : ''  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				} 0 .. $colmax ];  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		},  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		{  | 
| 
79
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
 			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
  
 | 
 
 | 
371
 | 
 use Data::TableReader::Iterator;  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
    | 
| 
93
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
298
 | 
 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
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	'row '.${ $f->{row_ref} };  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Data::TableReader::Decoder::Spreadsheet::_Iter::progress {  | 
| 
101
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
 	my $f= shift->_fields;  | 
| 
102
 | 
  
0
  
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
 	return ${ $f->{row_ref} } / (${ $f->{rowmax_ref} } || 1);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Data::TableReader::Decoder::Spreadsheet::_Iter::tell {  | 
| 
106
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
9
 | 
 	my $f= shift->_fields;  | 
| 
107
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	return [ $f->{sheet_idx}, ${$f->{row_ref}} ];  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Data::TableReader::Decoder::Spreadsheet::_Iter::seek {  | 
| 
111
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
6
 | 
 	my ($self, $to)= @_;  | 
| 
112
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	my $f= $self->_fields;  | 
| 
113
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
7
 | 
 	$to ||= $f->{origin};  | 
| 
114
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	my ($sheet_idx, $row)= @$to;  | 
| 
115
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 	my $sheet= $f->{sheets}[$sheet_idx];  | 
| 
116
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 	my ($colmin, $colmax)= $sheet? $sheet->col_range() : (0,-1);  | 
| 
117
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
18
 | 
 	my ($rowmin, $rowmax)= $sheet? $sheet->row_range() : (0,-1);  | 
| 
118
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 	$rowmax= _oo_rowmax_fix $sheet, $rowmax;  | 
| 
119
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 	$row= $rowmin-1 unless defined $row;  | 
| 
120
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	$f->{sheet_idx}= $sheet_idx;  | 
| 
121
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	${$f->{sheet_ref}}= $sheet;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
122
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
 	${$f->{row_ref}}= $row;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    | 
| 
123
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	${$f->{colmax_ref}}= $colmax;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
124
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 	${$f->{rowmax_ref}}= $rowmax;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
125
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 	1;  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub Data::TableReader::Decoder::Spreadsheet::_Iter::next_dataset {  | 
| 
129
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
3
 | 
 	my $self= shift;  | 
| 
130
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
 	my $f= $self->_fields;  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	return defined $f->{sheets}[ $f->{sheet_idx}+1 ]  | 
| 
132
 | 
1
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
24
 | 
 		&& $self->seek([ $f->{sheet_idx}+1 ]);  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |