File Coverage

lib/Spreadsheet/Reader/ExcelXML/Workbook.pm
Criterion Covered Total %
statement 160 173 92.4
branch 52 68 76.4
condition 5 9 55.5
subroutine 18 18 100.0
pod 2 4 50.0
total 237 272 87.1


line stmt bran cond sub pod time code
1             package Spreadsheet::Reader::ExcelXML::Workbook;
2             our $AUTHORITY = 'cpan:JANDREW';
3 16     16   801428 use version 0.77; our $VERSION = version->declare('v0.16.8');
  16         390  
  16         106  
4             ###LogSD warn "You uncovered internal logging statements for Spreadsheet::Reader::ExcelXML::Workbook-$VERSION";
5              
6 16     16   1577 use 5.010;
  16         37  
7 16     16   53 use Moose;
  16         21  
  16         105  
8 16     16   71344 use MooseX::StrictConstructor;
  16         10489  
  16         123  
9 16     16   36084 use MooseX::HasDefaults::RO;
  16         4441  
  16         138  
10 16     16   52824 use Carp qw( confess longmess );
  16         25  
  16         951  
11 16     16   65 use Clone 'clone';
  16         20  
  16         753  
12 16         147 use Types::Standard qw(
13             InstanceOf Str StrMatch Enum
14             HashRef ArrayRef CodeRef Int
15             HasMethods Bool is_Object is_HashRef
16             ConsumerOf is_ArrayRef
17 16     16   539 );
  16         46520  
18 16     16   22836 use lib '../../../../lib',;
  16         23  
  16         114  
19             ###LogSD use Log::Shiras::Telephone;
20             ###LogSD use Spreadsheet::Reader::ExcelXML::ZipReader;
21             ###LogSD use Spreadsheet::Reader::ExcelXML::Cell;
22             ###LogSD use Spreadsheet::Reader::ExcelXML::CellToColumnRow;
23             ###LogSD use Spreadsheet::Reader::ExcelXML::Chartsheet;
24             ###LogSD use Spreadsheet::Reader::ExcelXML::Error;
25             ###LogSD use Spreadsheet::Reader::ExcelXML::Row;
26             ###LogSD use Spreadsheet::Reader::ExcelXML::SharedStrings;
27             ###LogSD use Spreadsheet::Reader::ExcelXML::Styles;
28             ###LogSD use Spreadsheet::Reader::ExcelXML::WorkbookFileInterface;
29             ###LogSD use Spreadsheet::Reader::ExcelXML::WorkbookMetaInterface;
30             ###LogSD use Spreadsheet::Reader::ExcelXML::WorkbookPropsInterface;
31             ###LogSD use Spreadsheet::Reader::ExcelXML::WorkbookRelsInterface;
32             ###LogSD use Spreadsheet::Reader::ExcelXML::Worksheet;
33             ###LogSD use Spreadsheet::Reader::ExcelXML::WorksheetToRow;
34             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader;
35             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::FileWorksheet;
36             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::NamedSharedStrings;
37             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::NamedStyles;
38             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::NamedWorksheet;
39             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::PositionSharedStrings;
40             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::PositionStyles;
41             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookMeta;
42             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookProps;
43             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookRels;
44             ###LogSD use Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookXML;
45             ###LogSD use Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookMeta;
46             ###LogSD use Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookProps;
47             ###LogSD use Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookRels;
48 16         95 use MooseX::ShortCut::BuildInstance 1.040 qw(
49             build_instance should_re_use_classes set_args_cloning
50 16     16   2323 );
  16         375  
51             should_re_use_classes( 1 );
52             set_args_cloning ( 0 );
53 16     16   8280 use Spreadsheet::Reader::ExcelXML::Types qw( XLSXFile IOFileType is_XMLFile );
  16         27  
  16         112  
54             ###LogSD with 'Log::Shiras::LogSpace';
55              
56             #########1 Dispatch Tables 3#########4#########5#########6#########7#########8#########9
57              
58             my $parser_modules ={
59             workbook =>{
60             zip =>{
61             package => 'ZipWorkbookFile',
62             superclasses => ['Spreadsheet::Reader::ExcelXML::ZipReader'],
63             add_roles_in_sequence => [
64             'Spreadsheet::Reader::ExcelXML::WorkbookFileInterface'
65             ],
66             },
67             xml =>{
68             package => 'XMLWorkbookFile',
69             superclasses =>[ 'Spreadsheet::Reader::ExcelXML::XMLReader' ],
70             add_roles_in_sequence =>[
71             'Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookXML',
72             'Spreadsheet::Reader::ExcelXML::WorkbookFileInterface',
73             ],
74             },
75             },
76             workbook_meta_interface =>{
77             superclasses => ['Spreadsheet::Reader::ExcelXML::XMLReader'],
78             package => 'WorkbookMetaInterface',
79             differentiation =>{
80             zip =>{
81             file => 'xl/workbook.xml',
82             add_roles_in_sequence =>[
83             'Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookMeta',
84             'Spreadsheet::Reader::ExcelXML::WorkbookMetaInterface',
85             ],
86             },
87             xml =>{
88             file => [qw( ALL_FILE )],
89             add_roles_in_sequence =>[
90             'Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookMeta',
91             'Spreadsheet::Reader::ExcelXML::WorkbookMetaInterface',
92             ],
93             },
94             },
95             meta_load => [ qw( epoch_year sheet_list sheet_lookup rel_lookup id_lookup ) ],
96             },
97             workbook_rels_interface =>{
98             package => 'WorkbookRelsInterface',
99             superclasses => ['Spreadsheet::Reader::ExcelXML::XMLReader'],
100             differentiation =>{
101             zip =>{
102             file => 'xl/_rels/workbook.xml.rels',
103             add_roles_in_sequence =>[
104             'Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookRels',
105             'Spreadsheet::Reader::ExcelXML::WorkbookRelsInterface',
106             ],
107             },
108             xml =>{
109             file => [qw( NO_FILE )],
110             add_roles_in_sequence =>[
111             'Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookRels',
112             'Spreadsheet::Reader::ExcelXML::WorkbookRelsInterface',
113             ],
114             },
115             },
116             meta_load => [ qw( sheet_lookup worksheet_list chartsheet_list ) ],
117             },
118             workbook_props_interface =>{
119             package => 'WorkbookPropsInterface',
120             superclasses => ['Spreadsheet::Reader::ExcelXML::XMLReader'],
121             differentiation =>{
122             zip =>{
123             file => 'docProps/core.xml',
124             add_roles_in_sequence =>[
125             'Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookProps',
126             'Spreadsheet::Reader::ExcelXML::WorkbookPropsInterface',
127             ],
128             },
129             xml =>{
130             file => [qw( DocumentProperties )],
131             add_roles_in_sequence =>[
132             'Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookProps',
133             'Spreadsheet::Reader::ExcelXML::WorkbookPropsInterface',
134             ],
135             },
136             },
137             meta_load => [ qw( creator modified_by date_created date_modified ) ],
138             },
139             shared_strings_interface =>{
140             package => 'SharedStringsInterface',
141             superclasses => ['Spreadsheet::Reader::ExcelXML::XMLReader'],
142             differentiation =>{
143             zip =>{
144             file => 'xl/sharedStrings.xml',
145             add_roles_in_sequence =>[
146             'Spreadsheet::Reader::ExcelXML::XMLReader::PositionSharedStrings',
147             'Spreadsheet::Reader::ExcelXML::SharedStrings',
148             ],
149             },
150             xml =>{
151             file => [qw( SharedStrings )],
152             add_roles_in_sequence =>[
153             'Spreadsheet::Reader::ExcelXML::XMLReader::NamedSharedStrings',
154             'Spreadsheet::Reader::ExcelXML::SharedStrings',
155             ],
156             },
157             },
158             meta_load => [ qw( self ) ],
159             },
160             styles_interface =>{
161             package => 'StylesInstance',
162             superclasses => ['Spreadsheet::Reader::ExcelXML::XMLReader'],
163             differentiation =>{
164             zip =>{
165             file => 'xl/styles.xml',
166             add_roles_in_sequence =>[
167             'Spreadsheet::Reader::ExcelXML::XMLReader::PositionStyles',
168             'Spreadsheet::Reader::ExcelXML::Styles',
169             ],
170             },
171             xml =>{
172             file => [qw( Styles )],
173             add_roles_in_sequence =>[
174             'Spreadsheet::Reader::ExcelXML::XMLReader::NamedStyles',
175             'Spreadsheet::Reader::ExcelXML::Styles',
176             ],
177             },
178             },
179             meta_load => [ qw( self ) ],
180             },
181             worksheet_interface =>{
182             package => 'Worksheet',
183             superclasses => ['Spreadsheet::Reader::ExcelXML::XMLReader'],
184             differentiation =>{
185             zip =>{
186             add_roles_in_sequence =>[
187             'Spreadsheet::Reader::ExcelXML::CellToColumnRow',
188             'Spreadsheet::Reader::ExcelXML::XMLReader::FileWorksheet',
189             'Spreadsheet::Reader::ExcelXML::WorksheetToRow',
190             'Spreadsheet::Reader::ExcelXML::Worksheet'
191             ],
192             },
193             xml =>{
194             add_roles_in_sequence =>[
195             'Spreadsheet::Reader::ExcelXML::CellToColumnRow',
196             'Spreadsheet::Reader::ExcelXML::XMLReader::NamedWorksheet',
197             'Spreadsheet::Reader::ExcelXML::WorksheetToRow',
198             'Spreadsheet::Reader::ExcelXML::Worksheet'
199             ],
200             },
201             },
202             },
203             chartsheet_interface =>{
204             package => 'Chartsheet',
205             superclasses => ['Spreadsheet::Reader::ExcelXML::Chartsheet'],
206             },
207             };
208              
209             #########1 Public Attributes 3#########4#########5#########6#########7#########8#########9
210              
211             has error_inst =>(
212             isa => HasMethods[qw(
213             error set_error clear_error
214             set_warnings should_spew_longmess if_warn
215             spewing_longmess has_error
216             ),
217             ###LogSD 'set_log_space',
218             ],
219             clearer => '_clear_error_inst',
220             reader => 'get_error_inst',
221             predicate => 'has_error_inst',
222             handles =>[ qw(
223             error set_error clear_error
224             set_warnings should_spew_longmess if_warn
225             spewing_longmess has_error
226             ), ],
227             );
228              
229             has formatter_inst =>(
230             isa => ConsumerOf[ 'Spreadsheet::Reader::Format' ],# Interface
231             writer => 'set_formatter_inst',
232             reader => 'get_formatter_inst',
233             clearer => '_clear_formatter_inst',
234             predicate => '_has_formatter_inst',
235             handles => { qw(
236             get_formatter_region get_excel_region
237             has_target_encoding has_target_encoding
238             get_target_encoding get_target_encoding
239             set_target_encoding set_target_encoding
240             change_output_encoding change_output_encoding
241             set_defined_excel_formats set_defined_excel_formats
242             get_defined_conversion get_defined_conversion
243             parse_excel_format_string parse_excel_format_string
244             set_date_behavior set_date_behavior
245             set_european_first set_european_first
246             set_formatter_cache_behavior set_cache_behavior
247             set_workbook_for_formatter set_workbook_inst
248             ),
249             },
250             );
251             #~ get_excel_region get_excel_region
252              
253             has file =>(
254             isa => XLSXFile|IOFileType,
255             writer => 'set_file',
256             reader => '_file',
257             clearer => '_clear_file',
258             predicate => '_has_file',
259             trigger => \&build_workbook,
260             coerce => 1,
261             );
262              
263             has count_from_zero =>(
264             isa => Bool,
265             reader => 'counting_from_zero',
266             );
267              
268             has file_boundary_flags =>(
269             isa => Bool,
270             reader => 'boundary_flag_setting',
271             );
272              
273             has empty_is_end =>(
274             isa => Bool,
275             reader => 'is_empty_the_end',
276             );
277              
278             has values_only =>(
279             isa => Bool,
280             reader => 'get_values_only',
281             );
282              
283             has from_the_edge =>(
284             isa => Bool,
285             reader => 'starts_at_the_edge',
286             );
287              
288             has group_return_type =>(
289             isa => Enum[qw( unformatted value instance xml_value )],
290             reader => 'get_group_return_type',
291             );
292              
293             has empty_return_type =>(
294             isa => Enum[qw( empty_string undef_string )],
295             reader => 'get_empty_return_type',
296             );
297              
298             has cache_positions =>(
299             isa => HashRef,# broken -> Dict[ shared_strings_interface => Int, styles_interface => Int, worksheet_interface => Int ],
300             traits => ['Hash'],
301             reader => 'cache_positions',
302             writer => '_set_cache_positions',
303             handles =>{
304             _set_cache_size => 'set',
305             get_cache_size => 'get',
306             has_cache_size => 'exists'
307             },
308             );
309              
310             has show_sub_file_size =>(
311             isa => Bool,
312             reader => '_should_show_sub_file_size',
313             );
314              
315             has spread_merged_values =>(
316             isa => Bool,
317             reader => 'spreading_merged_values',
318             );
319              
320             has skip_hidden =>(
321             isa => Bool,
322             reader => 'should_skip_hidden',
323             );
324              
325             has spaces_are_empty =>(
326             isa => Bool,
327             reader => 'are_spaces_empty',
328             );
329              
330             has merge_data =>(
331             isa => Bool,
332             reader => 'collecting_merge_data',
333             );
334              
335             has column_formats =>(
336             isa => Bool,
337             reader => 'collecting_column_formats',
338             );
339              
340             #########1 Public Methods 3#########4#########5#########6#########7#########8#########9
341              
342             ###LogSD sub get_class_space{ 'Workbook' }
343              
344             sub worksheets{
345              
346 9     9 0 23755 my ( $self, ) = @_;
347             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
348             ###LogSD $self->get_all_space . '::worksheets', );
349             ###LogSD $phone->talk( level => 'info', message =>[
350             ###LogSD 'Attempting to build all worksheets: ', $self->get_worksheet_names ] );
351 9         17 my @worksheet_array;
352 9         38 while( my $worksheet_object = $self->worksheet ){
353             ###LogSD $phone->talk( level => 'info', message =>[
354             ###LogSD 'Built worksheet: ' . $worksheet_object->get_name ] );
355 19         74 push @worksheet_array, $worksheet_object;#$self->worksheet( $worksheet_name );
356             }
357             ###LogSD $phone->talk( level => 'trace', message =>[
358             ###LogSD 'sending worksheet array: ',@worksheet_array ] );
359 9         73 return @worksheet_array;
360             }
361              
362             sub worksheet{
363              
364 39     39 0 464 my ( $self, $worksheet_name ) = @_;
365 39         53 my ( $next_position );
366             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
367             ###LogSD $self->get_all_space . '::worksheet', );
368             ###LogSD $phone->talk( level => 'info', message =>[
369             ###LogSD "Arrived at (build a) worksheet with: ", $worksheet_name ] );
370              
371             # Handle an implied 'next sheet'
372 39 100       113 if( !$worksheet_name ){
373 28         875 my $worksheet_position = $self->_get_current_worksheet_position;
374             ###LogSD $phone->talk( level => 'info', message =>[
375             ###LogSD "No worksheet name passed",
376             ###LogSD ((defined $worksheet_position) ? "Starting after position: $worksheet_position" : '')] );
377 28 100       753 $next_position = ( !$self->in_the_list ) ? 0 : ($self->_get_current_worksheet_position + 1);
378             ###LogSD $phone->talk( level => 'info', message =>[
379             ###LogSD "No worksheet name passed", "Attempting position: $next_position" ] );
380 28 100       830 if( $next_position >= $self->worksheet_count ){
381             ###LogSD $phone->talk( level => 'info', message =>[
382             ###LogSD "Reached the end of the worksheet list" ] );
383 9         32 return undef;
384             }
385 19         577 $worksheet_name = $self->worksheet_name( $next_position );
386             ###LogSD $phone->talk( level => 'info', message =>[
387             ###LogSD "Updated worksheet name: $worksheet_name", ] );
388             }
389              
390             # Deal with chartsheet requests
391 30         954 my $worksheet_info = $self->get_sheet_info( $worksheet_name );
392             ###LogSD $phone->talk( level => 'debug', message =>[
393             ###LogSD "Info for the worksheet -$worksheet_name- is:", $worksheet_info, ] );
394 30 100       106 $next_position = $worksheet_info->{sheet_position} if !defined $next_position;
395             # Check for sheet existence
396 30 50 33     370 if( !$worksheet_info or !$worksheet_info->{sheet_type} ){
    100 66        
397 0         0 $self->set_error( "The worksheet -$worksheet_name- could not be located!" );
398 0         0 return undef;
399             }elsif( $worksheet_info->{sheet_type} and $worksheet_info->{sheet_type} eq 'chartsheet' ){
400 1         13 $self->set_error( "You have requested -$worksheet_name- which is a 'chartsheet' using a worksheet focused method" );
401 1         5 return undef;
402             }
403             # NOTE: THE CHARTSHEET / WORKSHEET COMMON SUB-METHOD COULD PROBABLY START HERE
404             ###LogSD $phone->talk( level => 'info', message =>[
405             ###LogSD "Building: $worksheet_name", "..with data:", $worksheet_info ] );
406              
407             # Check for a file and an available parser type then build the worksheet
408 29         43 my $worksheet;
409 29 50       799 confess "No file loaded yet" if !$self->file_opened;
410 29         959 my $file_ref = clone( $parser_modules->{worksheet_interface} );
411             ###LogSD $phone->talk( level => 'trace', message =>[
412             ###LogSD "Merging general worksheet with this worksheet info" ] );
413 29         265 my $args = { %$file_ref, %$worksheet_info };
414             ###LogSD $phone->talk( level => 'trace', message =>[
415             ###LogSD "worksheet_interface build attempt with settings:", $args] );
416              
417             # Strip bad args
418 29         54 my $new_args;
419 29         68 for my $arg (qw( sheet_position package superclasses sheet_id file
420             sheet_name is_hidden sheet_type differentiation )){
421             ###LogSD $phone->talk( level => 'debug', message =>[
422             ###LogSD "Passing through the values for: $arg", ] );
423 261 50       513 $new_args->{$arg} = $args->{$arg} if exists $args->{$arg};
424             }
425              
426 29         116 $worksheet = $self->_build_file_interface( 'worksheet_interface', $new_args );
427             ###LogSD $phone->talk( level => 'trace', message =>[
428             ###LogSD "worksheet_interface build attempt returned:", $worksheet] );
429             # handle the worksheet if succesfull
430 29 50       101 if( $worksheet ){
431             ###LogSD $phone->talk( level => 'info', message =>[
432             ###LogSD "Successfully loaded: $worksheet_name",
433             ###LogSD "Setting the current worksheet position to: $next_position" ] );
434 29         4928 $self->_set_current_worksheet_position( $next_position );
435 29         465 return $worksheet;
436             }else{
437 0         0 $self->set_error( "Failed to build the object for worksheet: $worksheet_name" );
438 0         0 return undef;
439             }
440             }
441              
442             sub build_workbook{
443              
444 20     20 1 37 my ( $self, $file ) = @_;
445             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
446             ###LogSD $self->get_all_space . '::_hidden::build_workbook', );
447             ###LogSD $phone->talk( level => 'info', message =>[
448             ###LogSD 'Arrived at build_workbook for: ', $file ] );
449 20         119 $self->clear_error;
450 20         588 $self->start_at_the_beginning;
451 20         570 $self->_clear_workbook_file_interface;
452 20         508 $self->_set_epoch_year( 1900 );
453 20         548 $self->_clear_sheet_list;
454 20         520 $self->_clear_sheet_lookup;
455 20         505 $self->_clear_rel_lookup;
456 20         538 $self->_clear_id_lookup;
457 20         552 $self->_clear_worksheet_list;
458 20         554 $self->_clear_chartsheet_list;
459 20         520 $self->_clear_creator;
460 20         546 $self->_clear_modified_by;
461 20         516 $self->_clear_date_created;
462 20         549 $self->_clear_date_modified,
463             $self->_clear_shared_strings_interface;
464 20         526 $self->_clear_styles_interface;
465 20         487 $self->_clear_file_name;
466 20         505 $self->_set_successful( 0 );
467              
468             # Add workbook to formatter (There is a lot more sheet meta data based configuration available here)
469 20         128 $self->set_workbook_for_formatter( $self );
470              
471             #save any file name
472 20 50       923 if( XLSXFile->check( $file ) ){
473             ###LogSD $phone->talk( level => 'info', message =>[ "Storing the file name: $file" ] );
474 20         713 $self->_set_file_name( $file );
475             }else{
476             ###LogSD $phone->talk( level => 'info', message =>[ "Not an xlsx file: $file" ] );
477             }
478              
479             # Attempt to turn whatever is passed into an IOFileType
480             ###LogSD $phone->talk( level => 'info', message =>[ "attempting to coerce passed ref/value: " . (length( ref( $file ) ) > 0 ? ref( $file ) : $file) ] );
481 20         100 my $file_handle = IOFileType->coerce( $file );
482             ###LogSD $phone->talk( level => 'info', message =>[ "Successfully coerced passed value to an IOFileType" ] );
483 20 50       78 if( ref $file_handle ){
484             ###LogSD $phone->talk( level => 'info', message =>[
485             ###LogSD "Successfully created the file handle:", $file_handle ] );
486             }else{
487 0 0       0 my $error_message = length( $@ ) > 0 ? $@ : IOFileType->get_message( $file );
488             ###LogSD $phone->talk( level => 'info', message =>[ "saving error |$error_message|", ] );
489 0         0 $self->set_error( $error_message );
490 0         0 return undef;
491             }
492              
493             # Attempt to build both a zip and an xml style workbook file extractor to see if either works
494 20         38 my $result;
495 20         35 for my $key ( keys %{$parser_modules->{workbook}} ){
  20         102  
496 28         833 my $args_ref = clone( $parser_modules->{workbook}->{$key} );
497             ###LogSD $phone->talk( level => 'info', message =>[
498             ###LogSD "Initial handle:", $file ] );
499 28         503 open( my $clone_handle, "<&", $file_handle );# Do this so when a fail -> close sequence happens you still have an open filehandle
500             ###LogSD $phone->talk( level => 'info', message =>[
501             ###LogSD "Clone handle:", $clone_handle ] );
502 28         58 $args_ref->{file} = $clone_handle;
503 28         45 $args_ref->{workbook_inst} = $self;
504             ###LogSD $args_ref->{log_space} = $self->get_log_space;
505             ###LogSD $phone->talk( level => 'debug', message =>[
506             ###LogSD "Attempting to build a base -$key- workbook file reader" ] );
507             ###LogSD $phone->talk( level => 'trace', message =>[
508             ###LogSD '...with ref:', $args_ref] );
509 28         106 $result = build_instance( $args_ref );
510             ###LogSD $phone->talk( level => 'trace', message =>[
511             ###LogSD "Returned from building: $key" ] );
512 28 50       800 if( $result ){
513             ###LogSD $phone->talk( level => 'trace', message =>[
514             ###LogSD 'Workbook build attempt returned:', $result] );
515 28 100       863 if( $result->loaded_correctly ){
516             ###LogSD $phone->talk( level => 'debug', message =>[
517             ###LogSD 'workbook file succesfully integrated' ] );
518 19         121 $self->clear_error;
519 19         65 last;
520             }else{
521 9         19 $result = undef;
522             ###LogSD $phone->talk( level => 'debug', message =>[
523             ###LogSD "workbook file build attempt for -$key- failed" ] );
524             }
525             }
526 9 50       198 if( !$result ){
527             ###LogSD $phone->talk( level => 'debug', message =>[
528             ###LogSD "Unable to build the workbook as: $key" ] );
529             }
530             }
531             ###LogSD $phone->talk( level => 'trace', message =>[
532             ###LogSD 'Test for build success with:', $result,] );# ( $result ? $result->meta->dump(6) : undef)
533 20         620 $self->_clear_file;# Too clean?
534 20 100       65 if( !$result ){
535             ###LogSD $phone->talk( level => 'debug', message =>[
536             ###LogSD 'Base workbook load failed' ] );
537 1         6 $self->set_error( "|$file| didn't pass either the zip or xml file initial tests" );
538 1         27 $self->_clear_file_name;
539 1         47 return undef;
540             }
541 19         521 $self->_set_successful( 1 );
542             ###LogSD $phone->talk( level => 'debug', message =>[
543             ###LogSD "Setting the workbook interface of type: " . $result->get_file_type ] );
544 19         593 $self->_set_workbook_file_interface( $result );
545             ###LogSD $phone->talk( level => 'trace', message =>[
546             ###LogSD "Workbook interface set to: ", $result ] );
547              
548             # Extract the workbook top level info
549 19         75 for my $element ( qw(
550             workbook_meta_interface workbook_rels_interface workbook_props_interface
551             shared_strings_interface styles_interface ) ){
552             ###LogSD $phone->talk( level => 'debug', message =>[
553             ###LogSD "Processing workbook level element: $element" ] );
554 95         3876 my $file_ref = clone( $parser_modules->{$element} );
555             ###LogSD $phone->talk( level => 'debug', message =>[
556             ###LogSD "element ref cloned:", $file_ref ] );
557 95         218 my $meta_load = $file_ref->{meta_load};
558 95         133 delete $file_ref->{meta_load};
559 95         334 $result = $self->_build_file_interface( $element, $file_ref );
560             ###LogSD $phone->talk( level => 'debug', message =>[
561             ###LogSD "$element build attempt returned" ] );
562             ###LogSD $phone->talk( level => 'trace', message =>[ $result ] );
563 95 100       1746 if( $result ){
564 94 100 66     2859 if( is_Object( $result ) and $result->loaded_correctly ){
565             ###LogSD $phone->talk( level => 'debug', message =>[ "$element succesfully built", ] );
566 91 100       311 if( $meta_load->[0] eq 'self' ){
567             ###LogSD $phone->talk( level => 'debug', message => [
568             ###LogSD "Loading the whole instance to the worksheet" ] );
569 34         97 my $load_method = '_set_' . $element;
570 34         1050 $self->$load_method( $result );
571 34         69 shift @$meta_load;# I'm not sure why you would want to load anything else after but just in case
572             }
573 91         356 $self->_load_meta_data( $result, $meta_load );
574             ###LogSD $phone->talk( level => 'debug', message => [
575             ###LogSD "Meta data loaded" ] );
576             }else{
577             ###LogSD $phone->talk( level => 'debug', message =>[
578             ###LogSD "No $element available" ] );
579             };
580             }
581             }
582             ###LogSD $phone->talk( level => 'debug', message => [
583             ###LogSD "..and final sheet master lookup:", $self->_get_sheet_lookup, ] );
584              
585 19         607 return $self;
586             }
587              
588             sub demolish_the_workbook{
589 23     23 1 973 my ( $self ) = @_;
590             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
591             ###LogSD $self->get_all_space . '::_hidden::demolish_the_workbook', );
592             ###LogSD $phone->talk( level => 'debug', message => [
593             ###LogSD "Last recorded error: " . ($self->error//'none') ] );
594              
595 23 50       74 if( $self ){
596              
597 23 50       607 if( $self->_has_file ){
598 0         0 print "closing general file (Used handle)\n";
599             ###LogSD $phone->talk( level => 'debug', message => [
600             ###LogSD "Clearing the top level file (used handle)" ] );
601 0         0 $self->_clear_file;
602             }
603              
604 23 100       583 if( $self->has_error_inst ){
605             #~ print "closing the error instance\n";
606             ###LogSD $phone->talk( level => 'debug', message => [
607             ###LogSD "closing the error instance" ] );
608 22         558 $self->_clear_error_inst;
609             }
610              
611 23 100       789 if( $self->has_shared_strings_interface ){
612             #~ print "closing sharedStrings.xml\n";# . Dumper( $instance )
613             ###LogSD $phone->talk( level => 'debug', message => [
614             ###LogSD "Clearing the sharedStrings.xml file" ] );
615 14         402 $self->_clear_shared_strings_interface;
616             }
617              
618 23 100       1173 if( $self->has_styles_interface ){
619             #~ print "closing styles.xml\n";# . Dumper( $instance )
620             ###LogSD $phone->talk( level => 'debug', message => [
621             ###LogSD "Clearing the styles.xml file" ] );
622 18         489 $self->_clear_styles_interface;
623             }
624              
625 23 100       1847 if( $self->_has_workbook_file_interface ){
626             #~ print "closing workbook interface file\n";
627             ###LogSD $phone->talk( level => 'debug', message => [
628             ###LogSD "Clearing the base workbook file interface" ] );
629 18         530 $self->_clear_workbook_file_interface;
630             }
631              
632 23 100       1294 if( $self->_has_formatter_inst ){
633             #~ print "closing the formatter\n";
634             ###LogSD $phone->talk( level => 'debug', message => [
635             ###LogSD "Clearing the formatter" ] );
636 22         580 $self->_clear_formatter_inst;
637             }
638             }
639             #~ print "~Reader::LibXML closed\n";
640             }
641              
642             #########1 Private Attributes 3#########4#########5#########6#########7#########8#########9
643              
644             has _file_name =>(
645             isa => XLSXFile,
646             writer => '_set_file_name',
647             clearer => '_clear_file_name',
648             reader => 'file_name',
649             );
650              
651             has _successful =>(
652             isa => Bool,
653             writer => '_set_successful',
654             reader => 'file_opened',
655             );
656              
657             has _epoch_year =>(
658             isa => Enum[qw( 1900 1904 )],
659             writer => '_set_epoch_year',
660             reader => 'get_epoch_year',
661             predicate => 'has_epoch_year',
662             default => 1900,
663             );
664              
665             has _sheet_list =>(
666             isa => ArrayRef,
667             traits => ['Array'],
668             writer => '_set_sheet_list',
669             clearer => '_clear_sheet_list',
670             reader => 'get_sheet_names',
671             handles =>{
672             get_sheet_name => 'get',
673             sheet_count => 'count',
674             },
675             default => sub{ [] },
676             );
677              
678             has _sheet_lookup =>(
679             isa => HashRef,
680             traits => ['Hash'],
681             writer => '_set_sheet_lookup',
682             reader => '_get_sheet_lookup',
683             clearer => '_clear_sheet_lookup',
684             handles =>{
685             get_sheet_info => 'get',
686             _set_sheet_info => 'set',
687             },
688             default => sub{ {} },
689             );
690              
691             has _rel_lookup =>(
692             isa => HashRef,
693             traits => ['Hash'],
694             writer => '_set_rel_lookup',
695             reader => '_get_rel_lookup',
696             clearer => '_clear_rel_lookup',
697             handles =>{
698             get_rel_info => 'get',
699             },
700             default => sub{ {} },
701             );
702              
703             has _id_lookup =>(
704             isa => HashRef,
705             traits => ['Hash'],
706             writer => '_set_id_lookup',
707             reader => '_get_id_lookup',
708             clearer => '_clear_id_lookup',
709             handles =>{
710             get_id_info => 'get',
711             },
712             default => sub{ {} },
713             );
714              
715             has _worksheet_list =>(
716             isa => ArrayRef,
717             traits => ['Array'],
718             clearer => '_clear_worksheet_list',
719             writer => '_set_worksheet_list',
720             reader => 'get_worksheet_names',
721             handles =>{
722             worksheet_name => 'get',
723             worksheet_count => 'count',
724             },
725             default => sub{ [] },
726             );
727              
728             has _chartsheet_list =>(
729             isa => ArrayRef,
730             traits => ['Array'],
731             clearer => '_clear_chartsheet_list',
732             writer => '_set_chartsheet_list',
733             reader => 'get_chartsheet_names',
734             handles =>{
735             chartsheet_name => 'get',
736             chartsheet_count => 'count',
737             },
738             default => sub{ [] },
739             );
740              
741             has _file_creator =>(
742             isa => Str,
743             reader => 'creator',
744             writer => '_set_creator',
745             clearer => '_clear_creator',
746             );
747              
748             has _file_modified_by =>(
749             isa => Str,
750             reader => 'modified_by',
751             writer => '_set_modified_by',
752             clearer => '_clear_modified_by',
753             );
754              
755             has _file_date_created =>(
756             isa => StrMatch[qr/^\d{4}\-\d{2}\-\d{2}/],
757             reader => 'date_created',
758             writer => '_set_date_created',
759             clearer => '_clear_date_created',
760             );
761              
762             has _file_date_modified =>(
763             isa => StrMatch[qr/^\d{4}\-\d{2}\-\d{2}/],
764             reader => 'date_modified',
765             writer => '_set_date_modified',
766             clearer => '_clear_date_modified',
767             );
768              
769             has _shared_strings_interface =>(
770             isa => ConsumerOf[ 'Spreadsheet::Reader::ExcelXML::SharedStrings' ],
771             predicate => 'has_shared_strings_interface',
772             writer => '_set_shared_strings_interface',
773             reader => '_get_shared_strings_interface',
774             clearer => '_clear_shared_strings_interface',
775             handles =>{
776             'get_shared_string' => 'get_shared_string',
777             #~ 'start_the_ss_file_over' => 'start_the_file_over',
778             },
779             );
780              
781             has _styles_insterface =>(
782             isa => ConsumerOf[ 'Spreadsheet::Reader::ExcelXML::Styles' ],
783             writer => '_set_styles_interface',
784             reader => '_get_styles_interface',
785             clearer => '_clear_styles_interface',
786             predicate => 'has_styles_interface',
787             handles =>{
788             get_format => 'get_format',
789             },
790             );
791              
792             has _current_worksheet_position =>(
793             isa => Int,
794             writer => '_set_current_worksheet_position',
795             reader => '_get_current_worksheet_position',
796             clearer => 'start_at_the_beginning',
797             predicate => 'in_the_list',
798             );
799              
800             has _workbook_file_interface =>(
801             isa => ConsumerOf[ 'Spreadsheet::Reader::ExcelXML::WorkbookFileInterface' ],
802             writer => '_set_workbook_file_interface',
803             reader => '_get_workbook_file_interface',
804             clearer => '_clear_workbook_file_interface',
805             predicate => '_has_workbook_file_interface',
806             handles =>{qw(
807             _extract_file extract_file
808             _get_workbook_file_type get_file_type
809             )},
810             );
811              
812             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
813              
814             around set_formatter_inst => sub {
815             my ( $method, $self, $instance ) = @_;
816             ###LogSD my $phone = Log::Shiras::Telephone->new(
817             ###LogSD name_space => $self->get_all_space . '::_hidden::set_formatter_inst', );
818             ###LogSD $phone->talk( level => 'trace', message =>[ 'Arrived at set_formatter_inst' ] );
819              
820             # Set the workbook instance for observation
821             if( $instance->can( 'set_workbook_inst' ) ){
822             $instance->set_workbook_inst( $self );
823             ###LogSD $phone->talk( level => 'trace', message =>[ 'Finished setting the workbook instance in the formatter inst:', $instance->dump(2) ] );#meta->
824             }else{
825             confess "Unable to set the formatter instance because it does not have an available method to set the workbook instance";
826             }
827              
828             # Carry on
829             $self->$method( $instance );
830             };
831              
832             sub _build_file_interface{
833 124     124   212 my( $self, $interface_type, $file_ref, ) = @_;
834             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
835             ###LogSD $self->get_all_space . '::_hidden::_build_file_interface', );
836             ###LogSD $phone->talk( level => 'debug', message => [
837             ###LogSD "Building interface: $interface_type" ] );
838             ###LogSD $phone->talk( level => 'trace', message => [
839             ###LogSD "..with file definition ref:", $file_ref ] );
840 124 50       339 if( exists $file_ref->{differentiation} ){
841 124         747 my $sub_ref = $file_ref->{differentiation}->{$self->_get_workbook_file_type};
842 124         306 delete $file_ref->{differentiation};
843 124         474 my @key_list = keys %$sub_ref;
844 124         488 @$file_ref{ @key_list } = @$sub_ref{ @key_list };
845             ###LogSD $phone->talk( level => 'debug', message => [
846             ###LogSD "Updated file ref:", $file_ref ] );
847             }
848             ###LogSD $phone->talk( level => 'debug', message => [
849             ###LogSD "Extracting file:", $file_ref->{file} ] );
850             # Handle pulling multiple nodes for flat xml files or one file name for zip files
851 124 100       563 my @file_extract = is_ArrayRef( $file_ref->{file} ) ? @{$file_ref->{file}} : $file_ref->{file};
  20         102  
852 124         1009 my $file = $self->_extract_file( @file_extract );
853 124 100       343 if( $file ){
854             ###LogSD $phone->talk( level => 'debug', message => [
855             ###LogSD "Returned the file:", $file, "..of size: " . (-s $file) ] );
856 123 50       4714 if( $self->_should_show_sub_file_size ){
857 0         0 warn "Loading interface -$interface_type- with file (byte) size: " . (-s $file);
858 0 0       0 if( $self->get_cache_size( $interface_type ) ){
859 0         0 warn "against max allowable caching: " . $self->get_cache_size( $interface_type );
860             }else{
861 0         0 warn "The interface -$interface_type- does not currently have a non-caching path";
862             }
863             #~ warn "hit return to acknowledge!!!!";
864             #~ my $wait = <>;
865             }
866              
867             # Turn off caching for sub files over a defined size
868 123 100       3841 if( $self->has_cache_size( $interface_type ) ){
869             ###LogSD $phone->talk( level => 'debug', message => [
870             ###LogSD "Testing cache size for -$interface_type- with max of: " . $self->get_cache_size( $interface_type ) ] );
871 66 100       2661 $file_ref->{cache_positions} = -s $file > $self->get_cache_size( $interface_type ) ? 0 : 1 ;
872             ###LogSD $phone->talk( level => 'debug', message => [
873             ###LogSD "Resolved cache setting: $file_ref->{cache_positions}" ] );
874             }
875              
876 123         217 $file_ref->{file} = $file;
877             ###LogSD $file_ref->{log_space} = $self->get_log_space;
878             ###LogSD $phone->talk( level => 'debug', message => [
879             ###LogSD "Building an instance with (minus the workbook)" ] );
880             ###LogSD $phone->talk( level => 'trace', message => [ $file_ref ] );
881 123         222 $file_ref->{workbook_inst} = $self;
882             ###LogSD $phone->talk( level => 'debug', message => [
883             ###LogSD "Workbook instance added" ] );
884 123         480 my $built_instance = build_instance( $file_ref );
885             ###LogSD $phone->talk( level => 'debug', message => [ "Built instance"] );
886             ###LogSD $phone->talk( level => 'trace', message => [ $built_instance ] );
887             #~ $built_instance->set_workbook_inst( $self );
888 123         4536 return $built_instance;
889             }else{
890 1         9 $self->set_error( "Unable to load Spreadsheet::Reader::ExcelXML::XMLReader with the attribute: $interface_type" );
891 1         2 return undef;
892             }
893             }
894              
895             sub _load_meta_data{
896 91     91   159 my( $self, $interface, $meta_settings ) = @_;
897             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
898             ###LogSD $self->get_all_space . '::_hidden::_load_meta_data', );
899             ###LogSD $phone->talk( level => 'debug', message => [
900             ###LogSD "Loading the meta data for:", $meta_settings ] );
901 91         366 for my $method_base ( @$meta_settings ){
902 228         324 my $setter = '_set_' . $method_base;
903 228         237 my $getter = 'get_' . $method_base;
904             ###LogSD $phone->talk( level => 'debug', message => [
905             ###LogSD "Retrieving from instance with : $getter", ] );
906 228         5944 my $return = $interface->$getter;
907 228 100       392 if( defined $return ){
908             ###LogSD $phone->talk( level => 'debug', message => [
909             ###LogSD "..and loading to the parser with: $setter", $return ] );
910 224         6054 $self->$setter( $return );
911             }
912             }
913             }
914              
915             #########1 Phinish 3#########4#########5#########6#########7#########8#########9
916              
917 16     16   39896 no Moose;
  16         26  
  16         92  
918             __PACKAGE__->meta->make_immutable;
919              
920             1;
921              
922             #########1 Documentation 3#########4#########5#########6#########7#########8#########9
923             __END__
924              
925             =head1 NAME
926              
927             Spreadsheet::Reader::ExcelXML::Workbook - Complicated self referential worbook class
928              
929             =head1 DESCRIPTION (TL;DR)
930              
931             Don't use this module by itself. Because it has a bit of twisty self referencing, it won't
932             L<garbage collect|http://www.perlmonks.org/?node_id=1156896> the way you expect unless you
933             use L<Spreadsheet::Reader::ExcelXML> to manage the garbage collection. From a practical
934             standpoint the necessary end user attributes and methods in this code can be effectivly used
935             against L<Spreadsheet::Reader::ExcelXML>. Thanks Moose for L<making that easy
936             |Moose::Manual::Delegation/DEFINING A MAPPING>. As a consequence all the end user methods
937             and attributes contained in this module are documented in L<Spreadsheet::Reader::ExcelXML>
938             instead. (the down side is the raw code and the documentation are two different files)
939              
940             =head2 Methods
941              
942             There are a few methods exported by this class that are not meant to be used by the end user
943             of the package but will still be delegated to L<Spreadsheet::Reader::ExcelXML> in order to
944             handle the twisty self referencing. As a consequence they will be documented here. (and not
945             there)
946              
947             =head3 build_workbook( $file )
948              
949             =over
950              
951             B<Definition:> If the passed $file is not a file handle it will store the value for
952             retrieval by L<Spreadsheet::Reader::ExcelXML/file_name> later. It will then coerce
953             the $file into a file handle. At that point it will reset the whole workbook and extract
954             the meta data from the workbook file in preparation for reading the sheets.
955              
956             B<Accepts:> $file which can either be a full file path string or a file handle
957              
958             B<Returns:> a built and ready to use L<Spreadsheet::Reader::ExcelXML::Workbook> instance
959             or undef on fail. It does not return a L<Spreadsheet::Reader::Excel> object even when
960             called as an exported method in L<Spreadsheet::Reader::Excel>. (Part of the twisty
961             nature of this class)
962              
963             =back
964              
965             =head3 demolish_the_workbook
966              
967             =over
968              
969             B<Definition:> Perl would normally delay garbage cleanup for this class until the script
970             exits since this has twisty self references. In order to allow the class to close and
971             clear when it goes out of scope L<Spreadsheet::Reader::ExcelXML> has to manually clear
972             this class when it goes out of scope. This is the method it uses to do that.
973              
974             B<Accepts:> nothing
975              
976             B<Returns:> nothing but all the self referencing attributes in the instance are cleared
977             allowing perl garbage collection to work when the L<Spreadsheet::Reader::ExcelXML>
978             instance goes out of scope. (after this method is called)
979              
980             =back
981              
982             =head3 has_shared_strings_interface
983              
984             =over
985              
986             B<Definition:> Indicates if a shared_strings_interface file was loaded and is available for
987             content extraction
988              
989             B<Accepts:> nothing
990              
991             B<Returns:> true if the interface is stored
992              
993             =back
994              
995             =head3 get_shared_string
996              
997             Delegated from L<Spreadsheet::Reader::ExcelXML::SharedStrings/get_shared_string( $positive_intE<verbar>$name )>
998              
999             =head3 has_styles_interface
1000              
1001             =over
1002              
1003             B<Definition:> Indicates if a styles_interface file was loaded and is available for
1004             content extraction
1005              
1006             B<Accepts:> nothing
1007              
1008             B<Returns:> true if the interface is stored
1009              
1010             =back
1011              
1012             =head3 get_format
1013              
1014             Delegated from L<Spreadsheet::Reader::ExcelXML::SharedStrings/get_format( ($positionE<verbar>$name), [$header], [$exclude_header] )>
1015              
1016             =head1 SUPPORT
1017              
1018             =over
1019              
1020             L<github Spreadsheet::Reader::ExcelXML/issues
1021             |https://github.com/jandrew/p5-spreadsheet-reader-excelxml/issues>
1022              
1023             =back
1024              
1025             =head1 TODO
1026              
1027             =over
1028              
1029             B<1.> Nothing currently
1030              
1031             =back
1032              
1033             =head1 AUTHOR
1034              
1035             =over
1036              
1037             =item Jed Lund
1038              
1039             =item jandrew@cpan.org
1040              
1041             =back
1042              
1043             =head1 COPYRIGHT
1044              
1045             This program is free software; you can redistribute
1046             it and/or modify it under the same terms as Perl itself.
1047              
1048             The full text of the license can be found in the
1049             LICENSE file included with this module.
1050              
1051             This software is copyrighted (c) 2016 by Jed Lund
1052              
1053             =head1 DEPENDENCIES
1054              
1055             =over
1056              
1057             B<L<Spreadsheet::Reader::ExcelXML>> - 2003 xml style and 2007+ (.xlsx) excel sheet reader
1058              
1059             B<L<Spreadsheet::Reader::ExcelXML::Worksheet>> - Worksheet level interface
1060              
1061             B<L<Spreadsheet::Reader::ExcelXML::Cell>> - Cell level interface
1062              
1063             L<Archive::Zip> - 1.30
1064              
1065             L<Carp> - confess longmess
1066              
1067             L<Clone> - clone
1068              
1069             L<Data::Dumper>
1070              
1071             L<FileHandle>
1072              
1073             L<IO::File>
1074              
1075             L<IO::Handle>
1076              
1077             L<Modern::Perl> - 1.20150127
1078              
1079             L<Moose>
1080              
1081             L<MooseX::StrictConstructor>
1082              
1083             L<MooseX::HasDefaults::RO>
1084              
1085             L<MooseX::StrictConstructor>
1086              
1087             L<MooseX::ShortCut::BuildInstance>
1088              
1089             L<Spreadsheet::Reader::Format> - v0.2.010
1090              
1091             L<Spreadsheet::Reader::Format::FmtDefault>
1092              
1093             L<Spreadsheet::Reader::Format::ParseExcelFormatStrings>
1094              
1095             L<Type::Library> - 1.000
1096              
1097             L<Types::Utils>
1098              
1099             L<Types::Standard> - qw(
1100             InstanceOf Str StrMatch Enum
1101             HashRef ArrayRef CodeRef Int
1102             HasMethods Bool is_Object is_HashRef
1103             ConsumerOf
1104             )
1105              
1106             L<lib>
1107              
1108             L<perl 5.010|https://metacpan.org/pod/release/RGARCIA/perl-5.10.0/pod/perl.pod>
1109              
1110             L<strict>
1111              
1112             L<version> - 0.77
1113              
1114             L<warnings>
1115              
1116             L<Spreadsheet::Reader::ExcelXML::ZipReader>
1117              
1118             L<Spreadsheet::Reader::ExcelXML::CellToColumnRow>
1119              
1120             L<Spreadsheet::Reader::ExcelXML::Chartsheet>
1121              
1122             L<Spreadsheet::Reader::ExcelXML::Error>
1123              
1124             L<Spreadsheet::Reader::ExcelXML::Row>
1125              
1126             L<Spreadsheet::Reader::ExcelXML::SharedStrings>
1127              
1128             L<Spreadsheet::Reader::ExcelXML::Styles>
1129              
1130             L<Spreadsheet::Reader::ExcelXML::WorkbookFileInterface>
1131              
1132             L<Spreadsheet::Reader::ExcelXML::WorkbookMetaInterface>
1133              
1134             L<Spreadsheet::Reader::ExcelXML::WorkbookPropsInterface>
1135              
1136             L<Spreadsheet::Reader::ExcelXML::WorkbookRelsInterface>
1137              
1138             L<Spreadsheet::Reader::ExcelXML::WorksheetToRow>
1139              
1140             L<Spreadsheet::Reader::ExcelXML::XMLReader>
1141              
1142             L<Spreadsheet::Reader::ExcelXML::XMLReader::FileWorksheet>
1143              
1144             L<Spreadsheet::Reader::ExcelXML::XMLReader::NamedSharedStrings>
1145              
1146             L<Spreadsheet::Reader::ExcelXML::XMLReader::NamedStyles>
1147              
1148             L<Spreadsheet::Reader::ExcelXML::XMLReader::NamedWorksheet>
1149              
1150             L<Spreadsheet::Reader::ExcelXML::XMLReader::PositionSharedStrings>
1151              
1152             L<Spreadsheet::Reader::ExcelXML::XMLReader::PositionStyles>
1153              
1154             L<Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookMeta>
1155              
1156             L<Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookProps>
1157              
1158             L<Spreadsheet::Reader::ExcelXML::XMLReader::WorkbookRels>
1159              
1160             L<Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookMeta>
1161              
1162             L<Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookProps>
1163              
1164             L<Spreadsheet::Reader::ExcelXML::ZipReader::WorkbookRels>
1165              
1166             L<Spreadsheet::Reader::ExcelXML::Types> - qw( XLSXFile IOFileType is_XMLFile )
1167              
1168             =back
1169              
1170             =head1 SEE ALSO
1171              
1172             =over
1173              
1174             L<Spreadsheet::Read> - generic Spreadsheet reader
1175              
1176             L<Spreadsheet::ParseExcel> - Excel binary version 2003 and earlier (.xls files)
1177              
1178             L<Spreadsheet::XLSX> - Excel version 2007 and later
1179              
1180             L<Spreadsheet::ParseXLSX> - Excel version 2007 and later
1181              
1182             L<Log::Shiras|https://github.com/jandrew/Log-Shiras>
1183              
1184             =over
1185              
1186             All lines in this package that use Log::Shiras are commented out
1187              
1188             =back
1189              
1190             =back
1191              
1192             =cut
1193              
1194             #########1#########2 main pod documentation end 5#########6#########7#########8#########9