File Coverage

blib/lib/Excel/ValueReader/XLSX/Backend.pm
Criterion Covered Total %
statement 69 69 100.0
branch 7 12 58.3
condition 1 3 33.3
subroutine 13 13 100.0
pod 0 3 0.0
total 90 100 90.0


line stmt bran cond sub pod time code
1             package Excel::ValueReader::XLSX::Backend;
2 1     1   717 use utf8;
  1         2  
  1         6  
3 1     1   50 use 5.10.1;
  1         7  
4 1     1   7 use Moose;
  1         2  
  1         5  
5 1     1   7143 use Archive::Zip qw(AZ_OK);
  1         73464  
  1         67  
6 1     1   11 use Carp qw/croak/;
  1         2  
  1         1032  
7              
8             our $VERSION = '1.10';
9              
10             #======================================================================
11             # ATTRIBUTES
12             #======================================================================
13             has 'frontend' => (is => 'ro', isa => 'Excel::ValueReader::XLSX',
14             required => 1, weak_ref => 1,
15             handles => [qw/A1_to_num formatted_date/]);
16              
17              
18              
19             my %lazy_attrs = ( zip => 'Archive::Zip',
20             date_styles => 'ArrayRef',
21             strings => 'ArrayRef',
22             workbook_data => 'HashRef',
23             table_info => 'HashRef',
24             sheet_for_table => 'ArrayRef', );
25              
26             while (my ($name, $type) = each %lazy_attrs) {
27             has $name => (is => 'ro', isa => $type, builder => "_$name", init_arg => undef, lazy => 1);
28             }
29              
30              
31              
32              
33             #======================================================================
34             # ATTRIBUTE CONSTRUCTORS
35             #======================================================================
36              
37             sub _zip {
38 14     14   26 my $self = shift;
39              
40 14         424 my $xlsx_file = $self->frontend->xlsx;
41 14         100 my $zip = Archive::Zip->new;
42 14         457 my $result = $zip->read($xlsx_file);
43 14 50       65956 $result == AZ_OK or die "cannot unzip $xlsx_file";
44              
45 14         583 return $zip;
46             }
47              
48              
49             sub _table_info {
50 2     2   6 my ($self) = @_;
51              
52 2         3 my %table_info;
53 2         63 my @table_members = $self->zip->membersMatching(qr[^xl/tables/table\d+\.xml$]);
54 2         511 foreach my $table_member (map {$_->fileName} @table_members) {
  10         59  
55 10         73 my ($table_id) = $table_member =~ /table(\d+)\.xml/;
56 10         31 my $table_xml = $self->_zip_member_contents($table_member);
57 10         42 my ($name, $ref, $table_columns, $no_headers)
58             = $self->_parse_table_xml($table_xml); # defined in subclass
59 10 50       518 my $sheet_id = $self->sheet_for_table->[$table_id]
60             or croak "could not find sheet id for table $table_id";
61 10         53 $table_info{$name} = [$sheet_id, $table_id, $ref, $table_columns, $no_headers];
62             }
63              
64 2         68 return \%table_info;
65             }
66              
67              
68             sub _sheet_for_table {
69 2     2   5 my ($self) = @_;
70              
71 2         4 my @sheet_for_table;
72 2         63 my @rel_members = $self->zip->membersMatching(qr[^xl/worksheets/_rels/sheet\d+\.xml\.rels$]);
73 2         526 foreach my $rel_member (map {$_->fileName} @rel_members) {
  10         61  
74 10         77 my ($sheet_id) = $rel_member =~ /sheet(\d+)\.xml/;
75 10         29 my $rel_xml = $self->_zip_member_contents($rel_member);
76 10         53 my @table_ids = $self->_table_targets($rel_xml); # defined in subclass
77 10         125 $sheet_for_table[$_] = $sheet_id foreach @table_ids;
78             }
79              
80 2         82 return \@sheet_for_table;
81             }
82              
83              
84             # attribute constructors for _date_styles, _strings and _workbook_data are supplied in subclasses
85              
86             #======================================================================
87             # METHODS
88             #======================================================================
89              
90              
91             sub base_year {
92 156     156 0 1439 my ($self) = @_;
93 156         4814 return $self->workbook_data->{base_year};
94             }
95              
96             sub sheets {
97 34     34 0 90 my ($self) = @_;
98 34         1087 return $self->workbook_data->{sheets};
99             }
100              
101              
102              
103              
104             sub Excel_builtin_date_formats {
105 10     10 0 25 my @numFmt;
106              
107             # source : section 18.8.30 numFmt (Number Format) in ECMA-376-1:2016
108             # Office Open XML File Formats — Fundamentals and Markup Language Reference
109 10         21 $numFmt[14] = 'mm-dd-yy';
110 10         22 $numFmt[15] = 'd-mmm-yy';
111 10         19 $numFmt[16] = 'd-mmm';
112 10         20 $numFmt[17] = 'mmm-yy';
113 10         15 $numFmt[18] = 'h:mm AM/PM';
114 10         20 $numFmt[19] = 'h:mm:ss AM/PM';
115 10         14 $numFmt[20] = 'h:mm';
116 10         24 $numFmt[21] = 'h:mm:ss';
117 10         15 $numFmt[22] = 'm/d/yy h:mm';
118 10         17 $numFmt[45] = 'mm:ss';
119 10         16 $numFmt[46] = '[h]:mm:ss';
120 10         16 $numFmt[47] = 'mmss.0';
121              
122 10         58 return @numFmt;
123             }
124              
125             sub _zip_member_contents {
126 84     84   184 my ($self, $member) = @_;
127              
128 84 50       2701 my $contents = $self->zip->contents($member)
129             or die "no contents for member $member";
130 84         100547 utf8::decode($contents);
131              
132 84         285 return $contents;
133             }
134              
135             sub _zip_member_name_for_sheet {
136 32     32   76 my ($self, $sheet) = @_;
137              
138             # check that sheet name was given
139 32 50       68 $sheet or die "->values(): missing sheet name";
140              
141             # get sheet id
142 32         89 my $id = $self->sheets->{$sheet};
143 32 100 33     199 $id //= $sheet if $sheet =~ /^\d+$/;
144 32 50       69 $id or die "no such sheet: $sheet";
145              
146             # construct member name for that sheet
147 32         155 return "xl/worksheets/sheet$id.xml";
148             }
149              
150              
151             1;
152              
153             __END__
154              
155             =head1 NAME
156              
157             Excel::ValueReader::XLSX::Backend -- abstract class, parent for the Regex and LibXML backends
158              
159             =head1 DESCRIPTION
160              
161             L<Excel::ValueReader::XLSX> has two possible implementation backends for parsing
162             C<XLSX> files :
163             L<Excel::ValueReader::XLSX::Backend::Regex>, based on regular expressions, or
164             L<Excel::ValueReader::XLSX::Backend::LibXML>, based on the libxml2 library.
165             Both backends share some common features, so the present class implements those
166             common features. This is about internal implementation; it should be of no interest
167             to external users of the module.
168              
169             =head1 ATTRIBUTES
170              
171             A backend instance possesses the following attributes :
172              
173             =over
174              
175             =item frontend
176              
177             a weak reference to the frontend instance
178              
179             =item zip
180              
181             an L<Archive::Zip> instance for accessing the contents of the C<xlsx> file
182              
183             =item date_styles
184              
185             an array of numeric styles for presenting dates and times. Styles are either
186             Excel's builtin styles, or custom styles defined in the workbook.
187              
188             =item strings
189              
190             an array of all shared strings within the workbook
191              
192             =item workbook_data
193              
194             some metadata information about the workbook
195              
196             =back
197              
198              
199              
200             =head1 ABSTRACT METHODS
201              
202             Not defined in this abstract class, but implemented in subclasses.
203              
204             =over
205              
206             =item values
207              
208             Inspects all cells within the XSLX files and returns a bi-dimensional array of values.
209              
210              
211             =back
212              
213              
214              
215             =head1 AUTHOR
216              
217             Laurent Dami, E<lt>dami at cpan.orgE<gt>
218              
219             =head1 COPYRIGHT AND LICENSE
220              
221             Copyright 2021 by Laurent Dami.
222              
223             This library is free software; you can redistribute it and/or modify
224             it under the same terms as Perl itself.