File Coverage

lib/Spreadsheet/Reader/ExcelXML/Worksheet.pm
Criterion Covered Total %
statement 313 344 90.9
branch 200 244 81.9
condition 84 129 65.1
subroutine 27 27 100.0
pod 16 17 94.1
total 640 761 84.1


line stmt bran cond sub pod time code
1             package Spreadsheet::Reader::ExcelXML::Worksheet;
2             our $AUTHORITY = 'cpan:JANDREW';
3 18     18   40573 use version; our $VERSION = version->declare('v0.16.8');
  18         34  
  18         131  
4             ###LogSD warn "You uncovered internal logging statements for Spreadsheet::Reader::ExcelXML::Worksheet-$VERSION";
5              
6 18     18   8891 use Modern::Perl;
  18         7658  
  18         85  
7 18     18   2329 use Carp 'confess';
  18         36  
  18         975  
8 18     18   72 use Moose::Role;
  18         24  
  18         142  
9             requires qw(
10             set_error parse_excel_format_string get_used_position
11             get_excel_position _min_row _max_row
12             _max_col _min_col get_merge_map
13             get_custom_column_data parse_column_row _parse_column_row
14             get_custom_row_data get_group_return_type change_output_encoding
15             get_error_inst has_styles_interface has_max_row
16             get_new_column is_empty_the_end go_to_or_past_row
17             boundary_flag_setting has_max_col get_format
18             get_new_next_value has_new_row_inst get_values_only
19             get_new_row_all get_new_row_number
20             );
21             ###LogSD requires 'get_log_space', 'get_all_space';
22 18         175 use Types::Standard qw(
23             Bool HasMethods Enum
24             Int is_Int ArrayRef
25             is_ArrayRef HashRef is_HashRef
26             is_Object Str is_Str
27 18     18   67046 );# Int
  18         34  
28 18     18   21063 use lib '../../../../lib',;
  18         29  
  18         138  
29             ###LogSD use Log::Shiras::Telephone;
30              
31 18     18   9911 use Spreadsheet::Reader::ExcelXML::Cell;
  18         69  
  18         852  
32 18         178 use Spreadsheet::Reader::ExcelXML::Types qw(
33             SpecialDecimal SpecialZeroScientific
34             SpecialOneScientific SpecialTwoScientific
35             SpecialThreeScientific SpecialFourScientific
36             SpecialFiveScientific
37 18     18   129 );
  18         24  
38              
39             #########1 Dispatch Tables 3#########4#########5#########6#########7#########8#########9
40              
41             my $format_headers =[ qw(
42             cell_font cell_border cell_style
43             cell_fill cell_coercion cell_alignment
44             ) ];
45              
46             #########1 Public Attributes 3#########4#########5#########6#########7#########8#########9
47              
48             has sheet_type =>(
49             isa => Enum[ 'worksheet' ],
50             default => 'worksheet',
51             reader => 'get_sheet_type',
52             );
53              
54             has sheet_rel_id =>(
55             isa => Str,
56             reader => 'rel_id',
57             );
58              
59             has sheet_id =>(
60             isa => Int,
61             reader => 'sheet_id',
62             );
63              
64             has sheet_position =>(# XML position
65             isa => Int,
66             reader => 'position',
67             );
68              
69             has sheet_name =>(
70             isa => Str,
71             reader => 'get_name',
72             predicate => '_has_name',
73             );
74              
75             has min_header_col =>(
76             isa => Int,
77             reader => 'get_min_header_col',
78             writer => 'set_min_header_col',
79             clearer => 'clear_min_header_col',
80             predicate => 'has_min_header_col'
81             );
82              
83             has max_header_col =>(
84             isa => Int,
85             reader => 'get_max_header_col',
86             writer => 'set_max_header_col',
87             clearer => 'clear_max_header_col',
88             predicate => 'has_max_header_col'
89             );
90              
91             #########1 Public Methods 3#########4#########5#########6#########7#########8#########9
92              
93             ###LogSD sub get_class_space{ 'Worksheet' }
94              
95             sub min_row{
96 1     1 1 12541 my( $self ) = @_;
97             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
98             ###LogSD $self->get_all_space . '::row_bound::min_row', );
99             ###LogSD $phone->talk( level => 'debug', message => [
100             ###LogSD "Returning the minimum row" ] );
101 1         48 my $code_min = $self->_min_row;
102             # Convert to user numbers
103 1         6 my $user_min = $self->get_used_position( $code_min );
104             ###LogSD $phone->talk( level => 'debug', message =>[
105             ###LogSD "Returning -$user_min- for row: $code_min" ] );
106 1         6 return $user_min;
107             }
108              
109             sub max_row{
110 2     2 1 5 my( $self ) = @_;
111             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
112             ###LogSD $self->get_all_space . '::row_bound::max_row', );
113             ###LogSD $phone->talk( level => 'debug', message => [
114             ###LogSD "Returning the maximum row" ] );
115 2         62 my $code_max = $self->_max_row;
116 2 100       6 if( defined $code_max ){
117             # Convert to user numbers
118 1         4 my $user_max = $self->get_used_position( $code_max );
119             ###LogSD $phone->talk( level => 'debug', message =>[
120             ###LogSD "Returning -$user_max- for row: $code_max" ] );
121 1         6 return $user_max;
122             }else{
123             ###LogSD $phone->talk( level => 'debug', message =>[
124             ###LogSD "No stored value for max row" ] );
125 1         5 return undef;
126             }
127             }
128              
129             sub min_col{
130 11     11 1 16 my( $self ) = @_;
131             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
132             ###LogSD $self->get_all_space . '::row_bound::min_col', );
133             ###LogSD $phone->talk( level => 'debug', message => [
134             ###LogSD "Returning the minimum column" ] );
135 11         285 my $code_min = $self->_min_col;
136             # Convert to user numbers
137 11         32 my $user_min = $self->get_used_position( $code_min );
138             ###LogSD $phone->talk( level => 'debug', message =>[
139             ###LogSD "Returning -$user_min- for column: $code_min" ] );
140 11         40 return $user_min;
141             }
142              
143             sub max_col{
144 75     75 1 71 my( $self ) = @_;
145             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
146             ###LogSD $self->get_all_space . '::row_bound::max_col', );
147             ###LogSD $phone->talk( level => 'debug', message => [
148             ###LogSD "Returning the maximum column" ] );
149 75         1680 my $code_max = $self->_max_col;
150 75 100       117 if( defined $code_max ){
151             # Convert to user numbers
152 74         173 my $user_max = $self->get_used_position( $code_max );
153             ###LogSD $phone->talk( level => 'debug', message =>[
154             ###LogSD "Returning -$user_max- for column: $code_max" ] );
155 74         228 return $user_max;
156             }else{
157             ###LogSD $phone->talk( level => 'debug', message =>[
158             ###LogSD "No stored value for max column" ] );
159 1         5 return undef;
160             }
161             }
162              
163             sub row_range{
164 10     10 1 3431 my( $self ) = @_;
165             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
166             ###LogSD $self->get_all_space . '::row_bound::row_range', );
167             ###LogSD $phone->talk( level => 'debug', message => [
168             ###LogSD "Returning row range" ] );
169 10         317 my $code_min = $self->_min_row;
170             # Convert to user numbers
171 10         39 my $user_min = $self->get_used_position( $code_min );
172             ###LogSD $phone->talk( level => 'debug', message =>[
173             ###LogSD "Returning -$user_min- for row: $code_min" ] );
174 10         247 my $code_max = $self->_max_row;
175 10         19 my $user_max;
176 10 100       36 if( defined $code_max ){
177             # Convert to user numbers
178 9         26 $user_max = $self->get_used_position( $code_max );
179             ###LogSD $phone->talk( level => 'debug', message =>[
180             ###LogSD "Returning -$user_max- for row: $code_max" ] );
181             }else{
182             ###LogSD $phone->talk( level => 'debug', message =>[
183             ###LogSD "No stored value for max row" ] );
184             }
185 10         42 return( $user_min, $user_max );
186             }
187              
188             sub col_range{
189 10     10 1 30 my( $self ) = @_;
190             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
191             ###LogSD $self->get_all_space . '::row_bound::col_range', );
192             ###LogSD $phone->talk( level => 'debug', message => [
193             ###LogSD "Returning col range" ] );
194 10         295 my $code_min = $self->_min_col;
195             # Convert to user numbers
196 10         43 my $user_min = $self->get_used_position( $code_min );
197             ###LogSD $phone->talk( level => 'debug', message =>[
198             ###LogSD "Returning -$user_min- for column: $code_min" ] );
199 10         286 my $code_max = $self->_max_col;
200 10         15 my $user_max;
201 10 100       34 if( defined $code_max ){
202             # Convert to user numbers
203 9         31 $user_max = $self->get_used_position( $code_max );
204             ###LogSD $phone->talk( level => 'debug', message =>[
205             ###LogSD "Returning -$user_max- for column: $code_max" ] );
206             }else{
207             ###LogSD $phone->talk( level => 'debug', message =>[
208             ###LogSD "No stored value for max row" ] );
209             }
210 10         38 return( $user_min, $user_max );
211             }
212              
213             sub get_merged_areas{
214 1     1 1 369 my( $self, ) = @_;
215             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
216             ###LogSD $self->get_all_space . '::get_merged_areas', );
217             ###LogSD $phone->talk( level => 'debug', message => [
218             ###LogSD 'Pulling the merge map ParseExcel style' ] );
219              
220             # Get the raw merge map
221 1         31 my $raw_map = $self->get_merge_map;
222             ###LogSD $phone->talk( level => 'trace', message =>[
223             ###LogSD "Raw merge row map;", $raw_map] );
224 1         2 my ( $new_map, $dup_ref );
225             #parse out the empty rows
226 1         2 for my $row ( @$raw_map ){
227 4 100       9 next if !$row;
228             ###LogSD $phone->talk( level => 'trace', message =>[
229             ###LogSD "Processing the merge row data:", $row] );
230 3         4 for my $merge_cell ( @$row ){
231 10 100       16 next if !$merge_cell;
232 6 100       13 next if exists $dup_ref->{$merge_cell};
233             ###LogSD $phone->talk( level => 'trace', message =>[
234             ###LogSD "Processing the merge row data: $merge_cell"] );
235 2         2 my $merge_ref;
236 2         6 for my $cell ( split /:/, $merge_cell ){
237 4         12 my ( $column, $row ) = $self->parse_column_row( $cell );
238 4         7 push @$merge_ref, $row, $column;
239             ###LogSD $phone->talk( level => 'trace', message =>[
240             ###LogSD "Updated merge ref:", $merge_ref] );
241             }
242 2         6 $dup_ref->{$merge_cell} = 1;
243 2         4 push @$new_map, $merge_ref;
244             ###LogSD $phone->talk( level => 'trace', message =>[
245             ###LogSD "Updated merge areas:", $new_map] );
246             }
247             }
248             ###LogSD $phone->talk( level => 'info', message =>[
249             ###LogSD "Final merge areas:", $new_map] );
250 1         8 return $new_map;
251             }
252              
253             sub is_column_hidden{
254 2     2 1 779 my( $self, @column_requests ) = @_;
255             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
256             ###LogSD $self->get_all_space . '::is_column_hidden', );
257             ###LogSD $phone->talk( level => 'debug', message => [
258             ###LogSD 'Pulling the hidden state for the columns:', @column_requests ] );
259              
260 2         2 my @result_list;
261 2         5 for my $item ( @column_requests ){
262 12         9 my $column;
263 12 100       26 if( is_Int( $item ) ){
264 6         51 $column = $self->get_excel_position( $item );
265             }else{
266 6         48 ( $column, my $dummy_row ) = $self->_parse_column_row( $item );
267             ###LogSD $phone->talk( level => 'trace', message => [
268             ###LogSD "Parsed -$item- to column number: $column" ] );
269             }
270 12         324 my $column_formats = $self->get_custom_column_data( $column );
271             push @result_list,
272             ( $column < $self->_min_col or $self->_max_col < $column ) ? undef :
273 12 100 33     255 ( $column_formats and exists $column_formats->{hidden}) ? $column_formats->{hidden} : 0;
    50 66        
274             }
275 2         4 my $true = 0;
276 2 100       4 map{ $true = 1 if $_ } @result_list;
  12         23  
277             ###LogSD $phone->talk( level => 'info', message =>[
278             ###LogSD "Final column hidden state is -$true- with list:", @result_list ] );
279 2 50       16 return wantarray ? @result_list : $true;
280             }
281              
282             sub is_row_hidden{
283 1     1 1 2 my( $self, @row_requests ) = @_;
284             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
285             ###LogSD $self->get_all_space . '::is_row_hidden', );
286             ###LogSD $phone->talk( level => 'debug', message => [
287             ###LogSD 'Pulling the hidden state for the rows:', [@row_requests] ] );
288              
289 1         2 my @result_list;
290 1         2 for my $row ( @row_requests ){
291 16         25 my $code_row = $self->get_excel_position( $row );
292             ###LogSD $phone->talk( level => 'debug', message =>[
293             ###LogSD "Sending excel row -$code_row- for used row: $row" ] );
294 16         398 my $row_formats = $self->get_custom_row_data( $code_row );
295             ###LogSD $phone->talk( level => 'debug', message =>[
296             ###LogSD "Returned row formats:", $row_formats ] );
297             push @result_list,
298             ( $code_row < $self->_min_row or $self->_max_row < $code_row ) ? undef :
299 16 100 66     333 ($row_formats and exists $row_formats->{hidden}) ? $row_formats->{hidden} : 0;
    100 66        
300             }
301 1         2 my $true = 0;
302 1 100       2 map{ $true = 1 if $_ } @result_list;
  16         34  
303             ###LogSD $phone->talk( level => 'info', message =>[
304             ###LogSD "Final row hidden state is -$true- with list:", @result_list] );
305 1 50       10 return wantarray ? @result_list : $true;
306             }
307              
308             sub get_cell{
309 356     356 1 137469 my ( $self, $requested_row, $requested_column ) = @_;
310             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
311             ###LogSD $self->get_all_space . '::get_cell', );
312             ###LogSD $phone->talk( level => 'info', message =>[
313             ###LogSD "Arrived at get_cell with: ",
314             ###LogSD "Requested row: " . (defined( $requested_row ) ? $requested_row : ''),
315             ###LogSD "Requested column: " . (defined( $requested_column ) ? $requested_column : '' ),] );
316              
317             # Ensure we have a good column and row to work from
318 356         385 my( $excel_requested_column, $excel_requested_row );
319 356 50       612 if( !defined $requested_row ){
320 0         0 $self->set_error( "No row provided" );
321 0         0 return undef;
322             }else{
323 356         919 $excel_requested_row = $self->get_excel_position( $requested_row );
324             ###LogSD $phone->talk( level => 'info', message =>[
325             ###LogSD "Updated row to (count from 1): $excel_requested_row", ] );
326             }
327 356 50       546 if( !defined $requested_column ){
328 0         0 $self->set_error( "No column provided" );
329 0         0 return undef;
330             }else{
331 356         616 $excel_requested_column = $self->get_excel_position( $requested_column );
332             ###LogSD $phone->talk( level => 'info', message =>[
333             ###LogSD "Updated column to (count from 1): $excel_requested_column", ] );
334             }
335              
336             # Get information
337             ###LogSD $phone->talk( level => 'trace', message =>[
338             ###LogSD 'Requesting [ $column, $row ]: [ ' . $excel_requested_column . ', ' . $excel_requested_row . ' ]' ] );
339 356         1096 my $current_row = $self->go_to_or_past_row( $excel_requested_row );
340 356         383 my $current_column;
341 356 100       860 if( is_Int( $current_row ) ){
342             ###LogSD $phone->talk( level => 'trace', message =>[
343             ###LogSD "Arrived at row: $current_row", "..searching for column: $excel_requested_column" ] );
344 348 100 66     3698 $current_column =
    100          
345             ( $current_row == $excel_requested_row ) ? $self->get_new_column( $excel_requested_column ) :
346             ( $self->has_max_col and $requested_column > $self->max_col) ? 'EOR' : undef ;
347             ###LogSD $phone->talk( level => 'trace', message =>[
348             ###LogSD "Column search result: ", ($current_column//'undef') ] );
349             }else{
350             ###LogSD $phone->talk( level => 'trace', message =>[
351             ###LogSD "Requested a row past the end of the file" ] );
352             }
353              
354             # Handle returns including EOF EOD and EOR flags
355 356         446 my $return = undef;
356 356 100 100     1074 if( $current_column and is_HashRef( $current_column ) ){
357             ###LogSD $phone->talk( level => 'trace', message =>[ 'Got a cell to build and return' ] );
358 190         1200 $return = $self->_build_out_the_cell( $current_column );
359             }else{
360 166 100       547 $return =
    100          
361             $current_column ? $current_column :
362             is_Int( $current_row ) ? undef : $current_row;
363             ###LogSD $phone->talk( level => 'trace', message =>[ "Initial boundary flag: " . ($return//'undef') ] );
364 166 100       547 if( $return ){# Handle some exceptions
365 86 100       153 if( $return eq 'EOD' ){# End of data equals end of row exception
366 58 50       253 if( $self->is_empty_the_end ){
367             ###LogSD $phone->talk( level => 'trace', message =>[ "Changing End Of Data to EOR" ] );
368 0         0 $return = 'EOR';
369             }else{
370             ###LogSD $phone->talk( level => 'trace', message =>[ "Clearing EOD" ] );
371 58         290 $return = undef;
372             }
373             }
374             #~ if( $return eq 'EOR' and $self->has_max_row and $self->max_row == $requested_row ){# EOR is EOF exception
375             #~ ###LogSD $phone->talk( level => 'trace', message =>[ "Changing EOR to EOF" ] );
376             #~ $return = 'EOF';
377             #~ } # Should return EOF from the file reader here?
378             # Adjust reported row column
379 86 100       200 ( $excel_requested_column, $excel_requested_row ) =
    100          
380             !$return ? ( $excel_requested_column, $excel_requested_row ) :
381             $return eq 'EOF' ? ( 0, 0 ) :
382             ( 0, $excel_requested_row + 1 ) ;
383             }
384             # Clear boundary flags per request
385 166 100 100     414 if( $return and !$self->boundary_flag_setting ){
386             ###LogSD $phone->talk( level => 'trace', message =>[ "No boundary flag settings desired" ] );
387 2         21 $return = undef;
388             }
389             }
390             ###LogSD $phone->talk( level => 'trace', message =>[ "Cell search returning:", $return ] );
391              
392 356         9531 $self->_set_reported_row_col( [ $excel_requested_row, $excel_requested_column ] );
393             ###LogSD $phone->talk( level => 'debug', message =>[
394             ###LogSD "Set the reported [ row, col ] to: [ $excel_requested_row, $excel_requested_column ]", ] );
395             ###LogSD $phone->talk( level => 'trace', message =>[ 'Final return:', $return ] );
396 356         2251 return $return;
397             }
398              
399             sub get_next_value{
400 76     76 1 31576 my ( $self, ) = @_;
401             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
402             ###LogSD $self->get_all_space . '::get_next_value', );
403             ###LogSD $phone->talk( level => 'info', message =>[ 'Arrived at get_next_value', ] );
404              
405             # Get next position
406 76         109 my $next_cell;
407 76 100       2708 if( $self->has_new_row_inst ){
408             ###LogSD $phone->talk( level => 'info', message =>[
409             ###LogSD "Current row number: " . $self->get_new_row_number, ] );
410 70         174 while( !$next_cell ){# To handle values only in a pre-cached environment
411 72         263 $next_cell = $self->get_new_next_value;
412             ###LogSD $phone->talk( level => 'debug', message =>[
413             ###LogSD 'Returned from next search with cell:', $next_cell ] );
414 72 100 100     260 if( $self->get_values_only and is_HashRef( $next_cell ) and
      66        
      66        
415             ( (!exists $next_cell->{cell_xml_value} and !exists $next_cell->{cell_unformatted}) or
416             (exists $next_cell->{cell_xml_value} and length( $next_cell->{cell_xml_value} ) == 0 ) ) ){
417 2         55 $next_cell = undef;
418             ###LogSD $phone->talk( level => 'debug', message =>[ 'Deleting this cell since it is empty' ] );
419             }
420             }
421             }else{
422             ###LogSD $phone->talk( level => 'debug', message =>[
423             ###LogSD 'No row instance currently loaded' ] );
424             }
425 76 100 100     1359 if( !$next_cell or $next_cell eq 'EOR' ){ # Handle no current row or no cell left case
426 34         42 $next_cell = undef;# Clear 'EOR'
427 34         116 my $current_row = $self->go_to_or_past_row;
428             ###LogSD $phone->talk( level => 'debug', message =>[
429             ###LogSD 'Updated Row: ' . $current_row] );
430 34         75 while( !$next_cell ){# To handle values only in a pre-cached environment
431 35 100       145 $next_cell = $current_row eq 'EOF' ? 'EOF' : $self->get_new_next_value;
432             ###LogSD $phone->talk( level => 'debug', message =>[
433             ###LogSD 'Returned from next search with cell:', $next_cell ] );
434              
435             # Handle EOF here
436 35 100       94 if( $next_cell eq 'EOF' ){
437             ###LogSD $phone->talk( level => 'debug', message =>[ 'Reached the EOF' ] );
438 6         171 $self->_set_reported_row_col( [ 0, 0 ] );
439 6 100       48 return $self->boundary_flag_setting ? $next_cell : undef;
440             }
441              
442             # Skip empty cells based on get_values_only
443 29 100 66     94 if( $self->get_values_only and is_HashRef( $next_cell ) and
      66        
      66        
444             ( (!exists $next_cell->{cell_xml_value} and !exists $next_cell->{cell_unformatted}) or
445             (exists $next_cell->{cell_xml_value} and length( $next_cell->{cell_xml_value} ) == 0 ) ) ){
446 1         21 $next_cell = undef;
447             ###LogSD $phone->talk( level => 'debug', message =>[ 'Deleting this cell since it is empty' ] );
448             }
449             }
450             }
451              
452             # Build out the cell
453             ###LogSD $phone->talk( level => 'trace', message =>[
454             ###LogSD 'Reporting the current row col for cell:', $next_cell ] );
455 70         2295 $self->_set_reported_row_col( [ $next_cell->{cell_row}, $next_cell->{cell_col} ] );
456 70         208 my $cell_instance = $self->_build_out_the_cell( $next_cell );
457             ###LogSD $phone->talk( level => 'trace', message =>[
458             ###LogSD 'Built cell instance:', $cell_instance ] );
459              
460 70         334 return $cell_instance;
461             }
462              
463             sub fetchrow_arrayref{
464 148     148 1 152720 my ( $self, $row ) = @_;
465             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
466             ###LogSD $self->get_all_space . '::fetchrow_arrayref', );
467             ###LogSD $phone->talk( level => 'info', message =>[
468             ###LogSD "Arrived at fetchrow_arrayref for row: " . ((defined $row) ? $row : ''), ] );
469              
470             # Handle an implied next
471 148 100       401 if( !defined $row ){
472 138         4459 my $last_row = $self->_get_reported_row;
473 138         219 $row = $last_row + 1;# Even if a cell is not at the end was last reported
474             ###LogSD $phone->talk( level => 'debug', message =>[
475             ###LogSD "Resolved an implied 'next' row request to row: $row", ] );
476             }else{
477 10         48 $row = $self->get_excel_position( $row );
478             ###LogSD $phone->talk( level => 'info', message =>[
479             ###LogSD "Updated row to (count from 1): $row", ] );
480             }
481              
482             # Advance to the proper row
483             ###LogSD $phone->talk( level => 'trace', message =>[
484             ###LogSD "Requesting row: $row" ] );
485 148         561 my $current_row = $self->go_to_or_past_row( $row );
486             ###LogSD $phone->talk( level => 'trace', message =>[
487             ###LogSD "Arrived at row: $current_row" ] );
488              
489             # Pull the data
490 148 100 100     443 my $result =
    100          
491             (is_Int( $current_row) and $current_row == $row) ? $self->get_new_row_all :
492             $current_row eq 'EOF' ? 'EOF' : undef;
493             ###LogSD $phone->talk( level => 'debug', message =>[ 'Returned row ref;', $result ] );
494              
495             # Build the return
496 148         504 my $return = [];
497 148         232 my ( $reported_row, $reported_col ) = ( $row, undef );
498 148 100       259 if( $result ){
499 125 100       397 if( is_ArrayRef( $result ) ){
500 111         476 for my $cell ( @$result ){
501 446 100       23491 if( is_HashRef( $cell ) ){
502             ###LogSD $phone->talk( level => 'debug', message =>[
503             ###LogSD 'Building out the cell:', $cell ] );
504 354         1149 $reported_col = $cell->{cell_col};
505              
506             # Skip empty cells based on get_values_only
507 354 50 33     994 if( $self->get_values_only and is_HashRef( $cell ) and
      0        
      33        
508             ( (!exists $cell->{cell_xml_value} and !exists $cell->{cell_unformatted}) or
509             (exists $cell->{cell_xml_value} and length( $cell->{cell_xml_value} ) == 0 ) ) ){
510             ###LogSD $phone->talk( level => 'debug', message =>[ 'Deleting this cell since it is empty' ] );
511             }else{
512 354         1761 push @$return, $self->_build_out_the_cell( $cell, );
513             }
514             }else{
515 92         341 push @$return, $cell;
516             }
517             }
518             }else{# Handle EOF flag
519 14 100       154 $return = $self->boundary_flag_setting ? $result : undef;
520             ###LogSD $phone->talk( level => 'debug', message =>[
521             ###LogSD "Based on boundary flag setting EOF state returning: $return" ] );
522 14         91 ( $reported_row, $reported_col ) = ( 0, 0 );
523             }
524             }else{
525 23         546 $reported_col = $self->_max_col;
526             }
527              
528             # Handle full rows with empty_is_end = 0
529 148         18774 $self->_set_reported_row_col( [ $reported_row, $reported_col ] );
530             ###LogSD $phone->talk( level => 'debug', message =>[
531             ###LogSD "Set the reported [ row, col ] to: [ $reported_row, $reported_col ]", ] );
532             ###LogSD $phone->talk( level => 'trace', message =>[ 'Final return:', $return ] );
533              
534 148         1146 return $return;
535             }
536              
537             sub fetchrow_array{
538 15     15 1 10994 my ( $self, $row ) = @_;
539             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
540             ###LogSD $self->get_all_space . '::fetchrow_array', );
541             ###LogSD $phone->talk( level => 'info', message =>[
542             ###LogSD "Arrived at fetchrow_array for row: " . ((defined $row) ? $row : ''), ] );
543 15         30 my $array_ref = $self->fetchrow_arrayref( $row );
544             ###LogSD $phone->talk( level => 'trace', message =>[ 'Initial return:', $array_ref ] );
545 15 50       28 my @return =
    100          
546             is_ArrayRef( $array_ref ) ? @$array_ref :
547             is_Str( $array_ref ) ? $array_ref : ();
548             ###LogSD $phone->talk( level => 'trace', message =>[ 'Final return:', @return ] );
549 15         97 return @return;
550             }
551              
552             sub set_headers{
553 2     2 1 5 my ( $self, @header_row_list ) = @_;
554             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
555             ###LogSD $self->get_all_space . '::set_headers', );
556             ###LogSD $phone->talk( level => 'info', message =>[
557             ###LogSD "Arrived at set_headers for row: ", @header_row_list, ] );
558 2         3 my $header_ref;
559 2         63 $self->_clear_last_header_row;
560 2         47 $self->_clear_header_ref;
561 2         33 my $old_output = $self->get_group_return_type;
562             ###LogSD $phone->talk( level => 'info', message =>[
563             ###LogSD "Old output type: $old_output", ] );
564 2         30 my $new_output = $old_output;
565 2 50       8 if( $old_output eq 'instance' ){
566 0         0 $self->set_group_return_type( 'value' );
567 0         0 $new_output = 'value';
568             }else{
569 2         3 $old_output = undef;
570             }
571             ###LogSD $phone->talk( level => 'info', message =>[
572             ###LogSD "New output type: $new_output", ] );
573 2 50       6 if( scalar( @header_row_list ) == 0 ){
574 0         0 $self->set_error( "No row numbers passed to use as headers" );
575 0         0 return undef;
576             }
577 2         4 my $last_header_row = 0;
578 2         3 my $code_ref;
579 2         5 for my $row ( @header_row_list ){
580 2 50       5 if( ref( $row ) ){
581 0         0 $code_ref = $row;
582             ###LogSD $phone->talk( level => 'info', message =>[
583             ###LogSD "Found header manipulation code: ", $code_ref, ] );
584 0         0 next;
585             }
586 2 50       8 $last_header_row = $row if $row > $last_header_row;
587 2         9 my $array_ref = $self->fetchrow_arrayref( $row );
588             ###LogSD $phone->talk( level => 'info', message =>[
589             ###LogSD "Retreived header row -$row- with values: ", $array_ref, ] );
590 2         7 for my $x ( 0..$#$array_ref ){
591 10 50       25 $header_ref->[$x] = $array_ref->[$x] if !defined $header_ref->[$x];
592             }
593             ###LogSD $phone->talk( level => 'info', message =>[
594             ###LogSD "Updated header ref: ", $header_ref, ] );
595             }
596 2 50       9 if( $code_ref ){
597 0         0 my $scrubbed_headers;
598 0         0 for my $header ( @$header_ref ){
599 0         0 push @$scrubbed_headers, $code_ref->( $header );
600             }
601             ###LogSD $phone->talk( level => 'info', message =>[
602             ###LogSD "scrubbed header ref: ", $scrubbed_headers, ] );
603 0         0 $header_ref = $scrubbed_headers;
604             }
605 2         56 $self->_set_last_header_row( $last_header_row );
606 2         71 $self->_set_header_ref( $header_ref );
607 2 50       5 $self->set_group_return_type( $old_output ) if $old_output;
608 2         19 return $header_ref;
609             }
610              
611             sub fetchrow_hashref{
612 16     16 1 4023 my ( $self, $row ) = @_;
613             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
614             ###LogSD $self->get_all_space . '::fetchrow_hashref', );
615             ###LogSD $phone->talk( level => 'info', message =>[
616             ###LogSD "Arrived at fetchrow_hashref for row: " . ((defined $row) ? $row : ''), ] );
617             # Check that the headers are set
618 16 100 100     519 if( !$self->_has_header_ref ){
    100          
619 2         16 $self->set_error( "Headers must be set prior to calling fetchrow_hashref" );
620 2         11 return undef;
621             }elsif( defined $row and $row <= $self->get_last_header_row ){
622 2         47 $self->set_error(
623             "The requested row -$row- is at or above the bottom of the header rows ( " .
624             $self->get_last_header_row . ' )'
625             );
626 2         11 return undef;
627             }
628 12         38 my $array_ref = $self->fetchrow_arrayref( $row );
629 12 100 66     71 return $array_ref if !$array_ref or $array_ref eq 'EOF';
630 10         256 my $header_ref = $self->_get_header_ref;
631 10         32 my ( $start, $end ) = ( $self->min_col, $self->max_col );
632 10         249 my ( $min_col, $max_col ) = ( $self->get_min_header_col, $self->get_max_header_col );
633             ###LogSD $phone->talk( level => 'info', message =>[
634             ###LogSD ((defined $min_col) ? "Minimum header column: $min_col" : undef),
635             ###LogSD ((defined $max_col) ? "Maximum header column: $max_col" : undef), ] );
636 10 50 33     38 $min_col = ($min_col and $min_col>$start) ? $min_col - $start : 0;
637 10 50 33     46 $max_col = ($max_col and $max_col<$end) ? $end - $max_col : 0;
638             ###LogSD $phone->talk( level => 'info', message =>[
639             ###LogSD ((defined $min_col) ? "Minimum header column offset: $min_col" : undef),
640             ###LogSD ((defined $max_col) ? "Maximum header column offset: $max_col" : undef), ] );
641              
642             # Build the ref
643 10         11 my $return;
644 10         10 my $blank_count = 0;
645 10         22 for my $x ( (0+$min_col)..($self->max_col-$max_col) ){
646 40 50       67 my $header = defined( $header_ref->[$x] ) ? $header_ref->[$x] : 'blank_header_' . $blank_count++;
647             ###LogSD $phone->talk( level => 'info', message =>[
648             ###LogSD "Possibly adding value for header: $header" ] );
649 40 100       57 if( defined $array_ref->[$x] ){
650             ###LogSD $phone->talk( level => 'info', message =>[
651             ###LogSD "Adding value: $array_ref->[$x]" ] );
652 30         63 $return->{$header} = $array_ref->[$x];
653             }
654             }
655              
656 10         89 return $return;
657             }
658              
659             sub set_custom_formats{
660 3     3 1 204 my ( $self, @input_args ) = @_;
661 3         4 my $args;
662             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
663             ###LogSD $self->get_all_space . '::set_custom_formats', );
664             ###LogSD $phone->talk( level => 'info', message =>[
665             ###LogSD "Arrived at set_custom_formats with: ", @input_args, ] );
666 3         7 my $worksheet_custom = 0;
667 3 50 33     18 if( !@input_args ){
    50          
    50          
668 0         0 $self->( "The input args to 'set_custom_formts' are empty - no op" );
669 0         0 return undef;
670             }elsif( is_HashRef( $input_args[0] ) and @input_args == 1 ){
671 0         0 $args = $input_args[0];
672             }elsif( @input_args % 2 == 0 ){
673 3         29 $args = { @input_args };
674             }else{
675 0         0 $self->set_error( "Unable to coerce input args to a hashref: " . join( '~|~', @input_args ) );
676 0         0 return undef;
677             }
678             ###LogSD $phone->talk( level => 'info', message =>[
679             ###LogSD "Now acting on: ", $args ] );
680 3         5 my $final_load;
681 3         11 for my $key ( keys %$args ){
682 9         10 my $new_coercion;
683 9 50 33     71 if( $key eq '' or $key !~ /[A-Z]{0,3}(\d*)/ ){
    50          
684 0         0 $self->set_error( "-$key- is not an allowed custom format key" );
685 0         0 next;
686             }elsif( is_Object( $args->{$key} ) ){
687             ###LogSD $phone->talk( level => 'info', message =>[
688             ###LogSD "Key -$key- already has an object" ] );
689 0         0 $new_coercion = $args->{$key};
690             }else{
691             ###LogSD $phone->talk( level => 'info', message =>[
692             ###LogSD "Trying to build a new coercion for -$key- with: $args->{$key}" ] );
693 9         65 $new_coercion = $self->parse_excel_format_string( $args->{$key}, "Worksheet_Custom_" . $worksheet_custom++ );
694 9 50       5116 if( !$new_coercion ){
695 0         0 $self->set_error( "No custom coercion could be built for -$key- with: $args->{$key}" );
696 0         0 next;
697             }
698             ###LogSD $phone->talk( level => 'info', message =>[
699             ###LogSD "Built possible new coercion for -$key-" ] );
700             }
701 9 50       53 if( !$new_coercion->can( 'assert_coerce' ) ){
    50          
702 0         0 $self->set_error( "The identified coercion for -$key- cannot 'assert_coerce'" );
703             }elsif( !$new_coercion->can( 'display_name' ) ){
704 0         0 $self->set_error( "The custom coercion for -$key- cannot 'display_name'" );
705             }else{
706             ###LogSD $phone->talk( level => 'info', message =>[
707             ###LogSD "Loading -$key- with coercion: " . $new_coercion->display_name ] );
708 9         111 $final_load->{$key} = $new_coercion;
709             }
710             }
711 3         90 $self->_set_custom_format( %$final_load );
712             }
713              
714             #########1 Private Attributes 3#########4#########5#########6#########7#########8#########9
715              
716             has _last_header_row =>(
717             isa => Int,
718             reader => 'get_last_header_row',
719             writer => '_set_last_header_row',
720             clearer => '_clear_last_header_row',
721             predicate => 'header_row_set'
722             );
723              
724             has _custom_formats =>(
725             isa => HashRef[ HasMethods[ 'assert_coerce', 'display_name' ] ],
726             traits => ['Hash'],
727             reader => 'get_custom_formats',
728             default => sub{ {} },
729             clearer => '_clear_custom_formats',
730             handles =>{
731             has_custom_format => 'exists',
732             get_custom_format => 'get',
733             _set_custom_format => 'set',
734             },
735             );
736              
737             has _header_ref =>(
738             isa => ArrayRef,
739             writer => '_set_header_ref',
740             reader => '_get_header_ref',
741             clearer => '_clear_header_ref',
742             predicate => '_has_header_ref',
743             );
744              
745             has _reported_row_col =>(# Manage (store and retreive) in count from 1 mode
746             isa => ArrayRef[Int],
747             traits => ['Array'],
748             writer => '_set_reported_row_col',
749             reader => '_get_reported_row_col',
750             default => sub{ [ 0, 0 ] },# Pre-row and pre-col
751             handles =>{
752             _get_reported_row => [ get => 0 ],
753             _get_reported_col => [ get => 1 ],
754             },
755             );
756              
757             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
758              
759             sub _build_out_the_cell{
760 614     614   735 my ( $self, $result, ) = @_;
761             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
762             ###LogSD $self->get_all_space . '::_hidden::_build_out_the_cell', );
763             ###LogSD $phone->talk( level => 'debug', message =>[
764             ###LogSD "Building out the cell ref:", $result, "..with results as: ". $self->get_group_return_type, ] );
765 614         536 my ( $return, $hidden_format );
766 614 50       1000 if( is_HashRef( $result ) ){
    0          
767 614 100 66     4121 $return->{cell_xml_value} = $result->{cell_xml_value} if exists $result->{cell_xml_value} and defined $result->{cell_xml_value};
768 614 100 66     1423 $return->{cell_unformatted} = $result->{cell_unformatted} if exists $result->{cell_unformatted} and defined $result->{cell_unformatted};
769             ###LogSD $phone->talk( level => 'debug', message =>[
770             ###LogSD "processing cell object from cell ref:", $result ] );
771 614         575 my $scientific_format;
772 614 100 66     4621 if( !exists $return->{cell_unformatted} and exists $return->{cell_xml_value} and #Implement implied output formatting intrensic to Excel for scientific notiation
      100        
      66        
      66        
      66        
773             $return->{cell_xml_value} =~ /^(\-)?((\d{1,3})?(\.\d+)?)[Ee](\-)?(\d+)$/ and $2 and $6 and $6 < 309){# Maximum Excel value 1.79769313486232E+308 -> https://support.microsoft.com/en-us/kb/78113
774             #~ warn $return->{cell_xml_value};
775             ###LogSD $phone->talk( level => 'trace', message =>[
776             ###LogSD "Found special scientific notation case were stored values and visible values possibly differ" ] );
777 31 50       65 my $dec_sign = $1 ? $1 : '';
778 31         70 my $decimal = $2;
779 31 50       78 my $exp_sign = $5 ? $5 : '';
780 31         33 my $exponent = $6;
781 31         374 $decimal = sprintf '%.14f', $decimal;
782 31         143 $decimal =~ /([1-9])?\.(.*[1-9])?(0*)$/;
783 31 50       88 my $last_sig_digit =
    100          
784             !$2 ? 0 :
785             defined $3 ? 14 - length( $3 ) : 14 ;
786 31 50       79 my $initial_significant_digits = length( $exp_sign ) > 0 ? ($last_sig_digit + $exponent) : ($last_sig_digit - $exponent);
787             ###LogSD $phone->talk( level => 'debug', message =>[
788             ###LogSD "Processing decimal : $decimal",
789             ###LogSD "Final significant digit of the decimal is at: $last_sig_digit",
790             ###LogSD "Total significant digits : $initial_significant_digits", ] );
791 31 100       59 if( $initial_significant_digits > 19 ){
792             ###LogSD $phone->talk( level => 'debug', message =>[
793             ###LogSD "Attempting to use sprintf: %.${last_sig_digit}f", ] );
794 3         16 $return->{cell_unformatted} = $dec_sign . sprintf "%.${last_sig_digit}f", $decimal;
795 3         13 $return->{cell_unformatted} .= 'E' . $exp_sign . sprintf '%02d', $exponent;
796             ###LogSD $phone->talk( level => 'debug', message =>[
797             ###LogSD "Found the unformatted scientific notation case with result: $return->{cell_unformatted}"] );
798             }else{
799             #~ warn $initial_significant_digits if $initial_significant_digits < 0;# Uncomment here and26 lines up to validate the test 05-bad_xml_example_bug.t
800 28 50       53 $initial_significant_digits = $initial_significant_digits > -1 ? $initial_significant_digits : 0 ;# Fix 05-bad_xml_example_bug.t bug
801             ###LogSD $phone->talk( level => 'info', message =>[
802             ###LogSD "Attempting to use sprintf: %.${initial_significant_digits}f", ] );
803 28         195 $return->{cell_unformatted} = sprintf "%.${initial_significant_digits}f", $return->{cell_xml_value};
804             ###LogSD $phone->talk( level => 'debug', message =>[
805             ###LogSD "Found the unformatted decimal case with output: $return->{cell_unformatted}"] );
806             }
807 31         104 my $short_decimal = sprintf '%.5f', $decimal;
808 31         104 $short_decimal =~ /([1-9])?\.(.*[1-9])?(0*)$/;
809 31 50       66 my $short_sig_digit =
    100          
810             !$2 ? 0 :
811             defined $3 ? 5 - length( $3 ) : 5 ;
812              
813 31 50       169 $scientific_format =
    50          
    100          
    50          
    100          
    100          
    100          
814             ( $initial_significant_digits < 10 ) ? SpecialDecimal :
815             ( $short_sig_digit == 0 ) ? SpecialZeroScientific :
816             ( $short_sig_digit == 1 ) ? SpecialOneScientific :
817             ( $short_sig_digit == 2 ) ? SpecialTwoScientific :
818             ( $short_sig_digit == 3 ) ? SpecialThreeScientific :
819             ( $short_sig_digit == 4 ) ? SpecialFourScientific :
820             ( $short_sig_digit == 5 ) ? SpecialFiveScientific :
821             SpecialZeroScientific ;
822             ###LogSD $phone->talk( level => 'debug', message =>[
823             ###LogSD "Resolved the final formatted output to formatter: " . $scientific_format->display_name ] );
824             }
825              
826 614         983 $return->{cell_type} = $result->{cell_type};
827 614         826 $return->{r} = $result->{r};
828 614 100       1040 $return->{cell_merge} = $result->{cell_merge} if exists $result->{cell_merge};
829 614 100       1086 $return->{cell_hidden} = $result->{cell_hidden} if exists $result->{cell_hidden};
830 614 100 66     1603 if( !exists $return->{cell_unformatted} and exists $result->{cell_xml_value} ){
831 522         1225 @$return{qw( cell_unformatted rich_text )} = @$result{qw( cell_xml_value rich_text )};
832 522 100       1176 delete $return->{rich_text} if !$return->{rich_text};
833             ###LogSD $phone->talk( level => 'debug', message =>[
834             ###LogSD "No crazy number stuff the unformatted value is: $return->{cell_unformatted}"] );
835             }
836              
837             #Implement user defined changes in encoding
838             ###LogSD $phone->talk( level => 'debug', message =>[ "Current cell: ", $return ] );
839 614 100 66     2304 if( $return->{cell_unformatted} and length( $return->{cell_unformatted} ) > 0 ){
840 548         2000 $return->{cell_unformatted} = $self->change_output_encoding( $return->{cell_unformatted} );
841             ###LogSD $phone->talk( level => 'debug', message =>[
842             ###LogSD "Unformatted with output encoding changed: " . $return->{cell_unformatted} ] );# if defined $return->{cell_unformatted};
843             }
844 614 100       37076 if( $self->get_group_return_type eq 'unformatted' ){
845             ###LogSD $phone->talk( level => 'debug', message =>[
846             ###LogSD "Sending back just the unformatted value: " . ($return->{cell_unformatted}//'') ] ) ;
847 49         297 return $return->{cell_unformatted};
848             }
849 565 100 66     3719 if( $return->{cell_xml_value} and length( $return->{cell_xml_value} ) > 0 ){#Implement user defined changes in encoding
850 505         1305 $return->{cell_xml_value} = $self->change_output_encoding( $return->{cell_xml_value} );
851             ###LogSD $phone->talk( level => 'debug', message =>[
852             ###LogSD "XML with output encoding changed: " . $return->{cell_xml_value} ] );# if defined $return->{cell_unformatted};
853             }
854 565 50       26863 if( $self->get_group_return_type eq 'xml_value' ){
855             ###LogSD $phone->talk( level => 'debug', message =>[
856             ###LogSD "Sending back just the unformatted value: " . ($return->{cell_xml_value}//'') ] ) ;
857 0         0 return $return->{cell_xml_value};
858             }
859              
860             # Get any relevant custom format
861 565         1621 my $custom_format;
862 565 100       14669 if( $self->has_custom_format( $result->{r} ) ){
863             ###LogSD $phone->talk( level => 'debug', message =>[
864             ###LogSD "Custom format exists for: $result->{r}",] );
865 6         160 $custom_format = $self->get_custom_format( $result->{r} );
866             }else{
867 559         1436 $result->{r} =~ /([A-Z]+)(\d+)/;
868 559         1050 my ( $col_letter, $excel_row ) = ( $1, $2 );
869 559 50       13947 if( $self->has_custom_format( $col_letter ) ){
    100          
870             ###LogSD $phone->talk( level => 'debug', message =>[
871             ###LogSD "Custom format exists for column: $col_letter",] );
872 0         0 $custom_format = $self->get_custom_format( $col_letter );
873             }elsif( $self->has_custom_format( $excel_row ) ){
874             ###LogSD $phone->talk( level => 'debug', message =>[
875             ###LogSD "Custom format exists for row: $excel_row",] );
876 6         154 $custom_format = $self->get_custom_format( $excel_row );
877             }
878             }
879              
880             # Initial check for return of value only (custom format case)
881 565 100       1089 if( $custom_format ){
882             ###LogSD $phone->talk( level => 'debug', message =>[
883             ###LogSD 'Cell custom_format is:', $custom_format ] );
884 12 50       64 if( $self->get_group_return_type eq 'value' ){
885             ###LogSD $phone->talk( level => 'debug', message =>[
886             ###LogSD 'Applying custom format to: ' . $return->{cell_unformatted} ] );
887             ###LogSD $phone->talk( level => 'trace', message =>[
888             ###LogSD 'Returning value coerced by custom format:', $custom_format ] );
889             return Spreadsheet::Reader::ExcelXML::Cell->_return_value_only(
890             $return->{cell_unformatted},
891 0         0 $custom_format,
892             $self->get_error_inst,
893             ###LogSD $self->get_log_space . '::Cell::_hidden::_return_value_only',
894             );
895             }
896 12         108 $return->{cell_coercion} = $custom_format;
897 12         17 $return->{cell_type} = 'Custom';
898             }
899              
900             # handle the formula
901 565 50 66     1524 if( exists $result->{cell_formula} and defined $result->{cell_formula} and length( $result->{cell_formula} ) > 0 ){
      66        
902 48         88 $return->{cell_formula} = $result->{cell_formula};
903             }
904              
905             # convert the row column to user defined
906 565         1616 $return->{cell_row} = $self->get_used_position( $result->{cell_row} );
907 565         1148 $return->{cell_col} = $self->get_used_position( $result->{cell_col} );
908             ###LogSD $phone->talk( level => 'debug', message =>[
909             ###LogSD "Cell args to this point:", $return] );
910              
911 565 100       1056 if( exists $result->{s} ){
912 370 100       996 my $header = ($self->get_group_return_type eq 'value') ? 'cell_coercion' : undef;
913 370 100       1456 my $exclude_header = ($custom_format) ? 'cell_coercion' : undef;
914 370         385 my $format;
915 370 50 66     2364 if( $header and $exclude_header and $header eq $exclude_header ){
    100 33        
916             ###LogSD $phone->talk( level => 'info', message =>[
917             ###LogSD "It looks like you just want to just return the formatted value but there is already a custom format" ] );
918             }elsif( $self->has_styles_interface ){
919             ###LogSD $phone->talk( level => 'debug', message =>[
920             ###LogSD "Pulling formats with:", $result->{s}, $header, $exclude_header, ] );
921 346         2074 $format = $self->get_format( $result->{s}, $header, $exclude_header );
922             ###LogSD $phone->talk( level => 'trace', message =>[
923             ###LogSD "format is:", $format ] );
924             }else{
925 24         306 $self->set_error( "'s' element called out but the style file is not available!" );
926             }
927             ###LogSD $phone->talk( level => 'debug', message =>[
928             ###LogSD "Checking if the defined number format needs replacing with:", $custom_format, $scientific_format] );
929 370 100 66     1327 if( $custom_format ){
    100 66        
930             ###LogSD $phone->talk( level => 'debug', message =>[
931             ###LogSD "Custom formats override this cell", $custom_format->display_name] );
932 12         48 delete $format->{cell_coercion};
933             }elsif( $scientific_format and
934             ( !exists $format->{cell_coercion} or
935             $format->{cell_coercion}->display_name eq 'Excel_number_0' or
936             $format->{cell_coercion}->display_name eq 'Excel_text_0' ) ){
937             ###LogSD $phone->talk( level => 'debug', message =>[
938             ###LogSD "The generic number case will implement a hidden scientific format", $scientific_format] );
939 10         204 $return->{cell_coercion} = $scientific_format;
940 10         35 delete $format->{cell_coercion};
941             }
942             # Second check for value only - for the general number case not just custom formats
943 370 100       2915 if( $self->get_group_return_type eq 'value' ){
944             ###LogSD $phone->talk( level => 'debug', message =>[
945             ###LogSD 'Applying (a possible) regular format to: ' . ($return->{cell_unformatted}//''), $return, $format ] );
946             return Spreadsheet::Reader::ExcelXML::Cell->_return_value_only(
947             $return->{cell_unformatted},
948             $return->{cell_coercion} // $format->{cell_coercion},
949 198   33     1470 $self->get_error_inst,
950             ###LogSD $self->get_log_space . '::Cell::_hidden::_return_value_only',
951             );
952             }
953 172 100       1065 if( $self->has_styles_interface ){
954             ###LogSD $phone->talk( level => 'debug', message =>[
955             ###LogSD "Format headers are:", $format_headers ] );
956 148         525 for my $header ( @$format_headers ){
957 888 100       1702 if( exists $format->{$header} ){
958             ###LogSD $phone->talk( level => 'trace', message =>[
959             ###LogSD "Transferring styles header -$header- to the cell", ] );
960 597         816 $return->{$header} = $format->{$header};
961 597 100       899 if( $header eq 'cell_coercion' ){
962 126 100 100     622 if( $return->{cell_type} eq 'Numeric' and
963             $format->{$header}->name =~ /date/i){
964             ###LogSD $phone->talk( level => 'trace', message =>[
965             ###LogSD "Found a -Date- cell", ] );
966 22         200 $return->{cell_type} = 'Date';
967             }
968             }
969             }
970             }
971             ###LogSD $phone->talk( level => 'trace', message =>[
972             ###LogSD "Practice special old spreadsheet magic here as needed - for now only single quote in the formula bar", ] );
973 148 100       500 if( exists $format->{quotePrefix} ){
974             ###LogSD $phone->talk( level => 'debug', message =>[
975             ###LogSD "Found the single quote in the formula bar case", ] );# Other similar cases include carat and double quote in the formula bar (middle and right justified)
976 1         3 $return->{cell_alignment}->{horizontal} = 'left';
977 1 50       5 $return->{cell_formula} = $return->{cell_formula} ? ("'" . $return->{cell_formula}) : "'";
978             }
979              
980             }
981             }
982             ###LogSD $phone->talk( level => 'debug', message =>[
983             ###LogSD "Checking if a scientific format should be used", $scientific_format] );
984 367 100 100     1130 if( $scientific_format and !exists $return->{cell_coercion} ){
985             ###LogSD $phone->talk( level => 'debug', message =>[
986             ###LogSD "The generic number case will implement a hidden scientific format", $scientific_format] );
987 3         25 $return->{cell_coercion} = $scientific_format;
988             }
989              
990             ###LogSD $phone->talk( level => 'trace', message =>[
991             ###LogSD "Checking return type: " . $self->get_group_return_type, ] );
992             # Final check for value only - for the text case
993 367 100       1106 if( $self->get_group_return_type eq 'value' ){
994             ###LogSD $phone->talk( level => 'debug', message =>[
995             ###LogSD 'Applying (a possible) regular format to: |' . ($return->{cell_unformatted}//'') . '|' ] );
996             return Spreadsheet::Reader::ExcelXML::Cell->_return_value_only(
997             $return->{cell_unformatted},
998             $return->{cell_coercion},
999 118         567 $self->get_error_inst,
1000             ###LogSD $self->get_log_space . '::Cell::_hidden::_return_value_only',
1001             );
1002             }
1003 249         1455 $return->{error_inst} = $self->get_error_inst;
1004             ###LogSD $phone->talk( level => 'debug', message =>[
1005             ###LogSD "Current args ref is:", $return] );
1006             }elsif( $result ){
1007 0         0 confess "I don't know how I got here: $result";
1008             }
1009              
1010             # build a cell
1011 249 100       1696 delete $return->{cell_coercion} if !$return->{cell_coercion};#
1012             ###LogSD $return->{log_space} = $self->get_log_space;
1013             ###LogSD $phone->talk( level => 'debug', message =>[
1014             ###LogSD "Building cell with:", $return] );
1015 249         6391 my $cell = Spreadsheet::Reader::ExcelXML::Cell->new( %$return );
1016             ###LogSD $phone->talk( level => 'debug', message =>[
1017             ###LogSD "Cell is:", $cell ] );
1018 249         794 return $cell;
1019             }
1020              
1021             sub DEMOLISH{
1022 31     31 0 15038 my ( $self ) = @_;
1023             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1024             ###LogSD $self->get_all_space . '::_hidden::DEMOLISH', );
1025             ###LogSD if( $self->_has_name ){
1026             ###LogSD $phone->talk( level => 'debug', message =>[
1027             ###LogSD "Cleaning up worksheet: " . $self->get_name ] );
1028             ###LogSD }
1029 31 100       726 if( $self->has_file ){
1030             ###LogSD $phone->talk( level => 'debug', message =>[
1031             ###LogSD "Need to clear the file" ] );
1032 19         472 $self->clear_file;
1033             ###LogSD $phone->talk( level => 'debug', message =>[ "File cleared" ] );
1034             }
1035             }
1036              
1037             #########1 Phinish 3#########4#########5#########6#########7#########8#########9
1038              
1039 18     18   83952 no Moose::Role;
  18         35  
  18         175  
1040             1;
1041              
1042             #########1 Documentation 3#########4#########5#########6#########7#########8#########9
1043             __END__
1044              
1045             =head1 NAME
1046              
1047             Spreadsheet::Reader::ExcelXML::Worksheet - Top level Worksheet interface
1048              
1049             =head1 SYNOPSIS
1050              
1051             use strict;
1052             use warnings;
1053             use Data::Dumper;
1054              
1055             use Spreadsheet::Reader::ExcelXML;
1056             my $workbook = Spreadsheet::Reader::ExcelXML->new( #similar style to Spreadsheet::XLSX
1057             file => 't/test_files/TestBook.xlsx',# in the test folder of this package
1058             group_return_type => 'value',
1059             );
1060              
1061             if ( !$workbook->file_opened ) {
1062             die $workbook->error(), ".\n";
1063             }
1064              
1065             my $worksheet = $workbook->worksheet( 'Sheet5' );
1066             $worksheet->set_custom_formats( {
1067             2 =>'yyyy-mm-dd',
1068             } );
1069             my $value;
1070             while( !$value or $value ne 'EOF' ){
1071             $value = $worksheet->fetchrow_arrayref;
1072             print Dumper( $value );
1073             }
1074              
1075             ###########################
1076             # SYNOPSIS Output
1077             # $VAR1 = [ 'Superbowl Audibles', 'Column Labels' ];
1078             # $VAR1 = [ 'Row Labels', 2016-02-06', '2017-02-14', '2018-02-03', 'Grand Total' ];
1079             # $VAR1 = [ 'Blue', '10', '7', '', '17' ];
1080             # $VAR1 = [ 'Omaha', '', '', '2', '2' ];
1081             # $VAR1 = [ 'Red', '30', '5', '3', '38' ];
1082             # $VAR1 = [ 'Grand Total', '40', '12', '5', '57' ];
1083             # $VAR1 = 'EOF';
1084             ###########################
1085              
1086              
1087             The best example for use of this module alone is the test file in this package
1088             t/Spreadsheet/Reader/ExcelXML/10-worksheet.t
1089              
1090             =head1 DESCRIPTION
1091              
1092             This module is the worksheet interface. Even thought it will not provide all worksheet
1093             access itself it's role is to manage a consistent way of parsing Excel worksheets. If
1094             the sheet/tab is a 'chartsheet' then please review the documentation for L<Chartsheets
1095             |Spreadsheet::Reader::ExcelXML::Chartsheet>. The documentation in this file will include
1096             all publicly accesable elements. This include elements provided through other roles and
1097             the base class. The purpose of this instance is to extract worksheet level formats and
1098             cell level content for parsing. The workbook also has several L<Settings
1099             |Spreadsheet::Reader::ExcelXML/Attributes> that affect the outcome of methods from this
1100             interface. For example the workbook level attribute setting
1101             L<Spreadsheet::Reader::ExcelXML/count_from_zero> will directly affect how this data is
1102             returned when retreiving cell data. Please review all workbook level attribute
1103             documentation for information covering those possibilities. It is best to generate a
1104             worksheet instance from the workbook class using one of the various L<worksheet
1105             |Spreadsheet::Reader::ExcelXML/worksheet( $name )> methods.
1106              
1107             =head2 Modification of this Interface
1108              
1109             The final worksheet instance provided by this package is an amalgam of a base class and a
1110             few roles aggregated at run time based on attribute settings from the workbook level
1111             class. The easiest way to modify behaviour in this instance is to build an additional
1112             or replacement element and have it built into the interface by the workbook. The
1113             settings for this type of injection/adjustement are stored in the raw code of
1114             L<Spreadsheet::Reader::ExcelXML::Workbook> within the 'worksheet_interface' key of the
1115             $parser_modules variable. The file t/Spreadsheet/Reader/ExcelXML/10-worksheet.t
1116             in the distribution represents a good 'under the hood' example of the way all the elements
1117             are integrated into the larger worksheet class as a whole.
1118              
1119             =head2 Methods
1120              
1121             These are the various functions that are available to extract cell (values) to read or
1122             to extract worksheet level formats. There are several different methods provided to
1123             extract the same content in different ways. All the methods are object methods performed
1124             on the worksheet.
1125              
1126             B<Example:>
1127              
1128             my $cell_data = $worksheet->get_cell( $row, $column );
1129              
1130             =head3 min_row
1131              
1132             =over
1133              
1134             B<Definition:> This is the minimum row determined when the sheet is opened. This
1135             value is affected by the workbook attributes
1136             L<from_the_edge|Spreadsheet::Reader::ExcelXML/from_the_edge>, and
1137             L<count_from_zero|Spreadsheet::Reader::ExcelXML/count_from_zero>
1138              
1139             B<Accepts:> nothing
1140              
1141             B<Returns:> an integer $row
1142              
1143             =back
1144              
1145             =head3 has_min_row
1146              
1147             =over
1148              
1149             B<Definition:> The L<predicate|Moose::Manual::Attributes/Predicate and clearer methods>
1150             of min_row
1151              
1152             =back
1153              
1154             =head3 max_row
1155              
1156             =over
1157              
1158             B<Definition:> This is the maximum row with data listed in the sheet. This value
1159             is affected by the workbook attribute
1160             L<count_from_zero|Spreadsheet::Reader::ExcelXML/count_from_zero>. B<Warning:
1161             This value is extracted from the sheet metadata, however if your sheet has been
1162             damaged or 'adjusted' by non-microsoft code (This is more common than you would think
1163             in the data processing world) then this value may be wrong or missing when the sheet
1164             is first opened. The goal of this package is to minimize memory consumption so it
1165             will learn what the correct value is over the first pass through the sheet as you
1166             collect data but it does not attempt to validate this value in detail initially. If
1167             you have an idea of the range for a damaged sheet before you open it you can use
1168             L<EOF|change_boundary_flag( $Bool )> flags. Otherwise the methods
1169             L<get_next_value|/get_next_value> or L<fetchrow_arrayref|/fetchrow_arrayref> are
1170             recomended.>
1171              
1172             B<Accepts:> nothing
1173              
1174             B<Returns:> an integer
1175              
1176             =back
1177              
1178             =head3 has_max_row
1179              
1180             =over
1181              
1182             B<Definition:> The L<predicate|Moose::Manual::Attributes/Predicate and clearer methods>
1183             of max_row
1184              
1185             =back
1186              
1187             =head3 row_range
1188              
1189             =over
1190              
1191             B<Definition:> This returns a list containing the minimum row number followed
1192             by the maximum row number. This list is affected by the workbook attributes
1193             L<from_the_edge|Spreadsheet::Reader::ExcelXML/from_the_edge>, and
1194             L<count_from_zero|Spreadsheet::Reader::ExcelXML/count_from_zero> B<Warning:
1195             This result is extracted from the sheet metadata, however if your sheet has been
1196             damaged or 'adjusted' by non-microsoft code (This is more common than you would think
1197             in the data processing world) then the return list may be wrong or missing when the
1198             sheet is first opened. The goal of this package is to minimize memory consumption so it
1199             will learn what the correct list is over the first pass through the sheet as you
1200             collect data but it does not attempt to validate this list in detail initially. If
1201             you have an idea of the range for a damaged sheet before you open it you can use
1202             L<EOR-EOF|change_boundary_flag( $Bool )> flags. Otherwise the methods
1203             L<get_next_value|/get_next_value> or L<fetchrow_arrayref|/fetchrow_arrayref> are
1204             recomended.> For missing values the minimum is set to the first row and the maximum
1205             is set to undef.
1206              
1207             B<Accepts:> nothing
1208              
1209             B<Returns:> ( $minimum_row, $maximum_row )
1210              
1211             =back
1212              
1213             =head3 min_col
1214              
1215             =over
1216              
1217             B<Definition:> This is the minimum column with data listed in the sheet. This value
1218             is affected by the workbook attributes
1219             L<from_the_edge|Spreadsheet::Reader::ExcelXML/from_the_edge>, and
1220             L<count_from_zero|Spreadsheet::Reader::ExcelXML/count_from_zero>
1221              
1222             B<Accepts:> nothing
1223              
1224             B<Returns:> an integer
1225              
1226             =back
1227              
1228             =head3 has_min_col
1229              
1230             =over
1231              
1232             B<Definition:> The L<predicate|Moose::Manual::Attributes/Predicate and clearer methods>
1233             of min_col
1234              
1235             =back
1236              
1237             =head3 max_col
1238              
1239             =over
1240              
1241             B<Definition:> This is the maximum row with data listed in the sheet. This value
1242             is affected by the workbook attribute
1243             L<count_from_zero|Spreadsheet::Reader::ExcelXML/count_from_zero> B<Warning:
1244             This value is extracted from the sheet metadata, however if your sheet has been
1245             damaged or 'adjusted' by non-microsoft code (This is more common than you would think
1246             in the data processing world) then this value may be wrong or missing when the sheet
1247             is first opened. The goal of this package is to minimize memory consumption so it
1248             will learn what the correct value is over the first pass through the sheet as you
1249             collect data but it does not attempt to validate this value in detail initially. If
1250             you have an idea of the range for a damaged sheet before you open it you can use
1251             L<EOR|change_boundary_flag( $Bool )> flags. Otherwise the methods
1252             L<get_next_value|/get_next_value> or L<fetchrow_arrayref|/fetchrow_arrayref> are
1253             recomended.>
1254              
1255             B<Accepts:> nothing
1256              
1257             B<Returns:> an integer
1258              
1259             =back
1260              
1261             =head3 has_max_col
1262              
1263             =over
1264              
1265             B<Definition:> The L<predicate|Moose::Manual::Attributes/Predicate and clearer methods>
1266             of max_col
1267              
1268             =back
1269              
1270             =head3 col_range
1271              
1272             =over
1273              
1274             B<Definition:> This returns a list containing the minimum column number followed
1275             by the maximum column number. This list is affected by the workbook attributes
1276             L<from_the_edge|Spreadsheet::Reader::ExcelXML/from_the_edge>, and
1277             L<count_from_zero|Spreadsheet::Reader::ExcelXML/count_from_zero>
1278              
1279             B<Accepts:> nothing
1280              
1281             B<Returns:> ( $minimum_column, $maximum_column )
1282              
1283             =back
1284              
1285             =head3 get_merged_areas
1286              
1287             =over
1288              
1289             B<Definition:> This method returns an array ref of cells that are merged. This method does
1290             respond to the attribute L<Spreadsheet::Reader::ExcelXML/count_from_zero> B<Warning:
1291             This result is extracted from the sheet metadata for 2007+ Excel files, however if you
1292             are parsing an Excel 2003 xml file this data is stored at the cell level. Since this
1293             parser reads the data 'Just In Time' it will not know about a set of merged cells until the
1294             upper left cell of the group has been read.>
1295              
1296             B<Accepts:> nothing
1297              
1298             B<Returns:> An arrayref of arrayrefs of merged areas or undef if no merged areas
1299              
1300             [ [ $start_row_1, $start_col_1, $end_row_1, $end_col_1], etc.. ]
1301              
1302             =back
1303              
1304             =head3 is_column_hidden
1305              
1306             =over
1307              
1308             B<Definition:> Method indicates if the excel program would hide the identified column(s) or show
1309             it|them if the file were opened in the Microsoft Excel application. If more than one column is
1310             passed then it returns true if any of the columns are hidden in scalar context and a list of
1311             1 and 0 values for each of the requested positions in array (list) context. This method (input)
1312             does respond to the attribute L<Spreadsheet::Reader::ExcelXML/count_from_zero>. For 2003
1313             xml files this data is stored at the cell level so the parser will not 'know' until you have
1314             read (past) the cell.
1315              
1316             B<Accepts:> integer values or column letter values selecting the columns in question
1317              
1318             B<Returns:> in scalar context it returns a boolean value indicating if any of the requested
1319             columns would be hidden by Excel. In array/list context it returns a list of boolean values
1320             for each requested column indicating it's hidden state for Excel. (1 = hidden)
1321              
1322             B<Examples:> (Indicating the 3rd and 4th of 6 columns are hidden)
1323              
1324              
1325             $worksheet_instance->is_column_hidden( 0 .. 5 ),
1326             ###########################
1327             # Example Output
1328             # [ 0, 0, 1, 1, 0, 0 ]
1329             ###########################
1330              
1331             $worksheet_instance->is_column_hidden( 'A', 'B', 'C', 'D', 'E', 'F' )
1332             ###########################
1333             # Example Output
1334             # [ 0, 0, 1, 1, 0, 0 ]
1335             ###########################
1336              
1337             =back
1338              
1339             =head3 is_row_hidden
1340              
1341             =over
1342              
1343             B<Definition:> Method indicates if the excel program would hide the identified row(s) or show
1344             it|them if the file were opened in the Microsoft Excel application. If more than one row is
1345             passed then it returns true if any of the rows are hidden in scalar context and a list of
1346             1 and 0 values for each of the requested positions in array (list) context. This method (input)
1347             does respond to the attribute L<Spreadsheet::Reader::ExcelXML/count_from_zero>. B<Warning:
1348             This method will only be accurate after the user has read at least one cell from or past the row
1349             inspected for it's hidden state. This allows the sheet to avoid reading all the way through once
1350             before starting the cell parsing.>
1351              
1352             B<Accepts:> integer values selecting the rows in question
1353              
1354             B<Returns:> in scalar context it returns a boolean value indicating if any of the requested
1355             rows would be hidden by Excel. In array/list context it returns a list of boolean values
1356             for each requested row indicating it's hidden state for Excel. (1 = hidden)
1357              
1358             =back
1359              
1360             =head3 get_cell( $row, $column )
1361              
1362             =over
1363              
1364             B<Definition:> Indicate both the requested row and requested column and the information for
1365             that position will be returned. Both $row and $column are required
1366              
1367             B<Accepts:> the list ( $row, $column ) both required See the attribute
1368             L<Spreadsheet::Reader::ExcelXML/count_from_zero> to understand which row and column
1369             are returned for $row and $colum.
1370              
1371             B<Returns:> see the attribute L<Spreadsheet::Reader::ExcelXML/group_return_type> for
1372             details on what is returned
1373              
1374             =back
1375              
1376             =head3 get_next_value
1377              
1378             =over
1379              
1380             B<Definition:> Reading left to right and top to bottom this will return the next cell with
1381             a value. This can includes cells with no value but some unique formatting such as
1382             cells that have been merged with other cells. See the attributes
1383             L<Spreadsheet::Reader::ExcelXML/values_only> and
1384             L<Spreadsheet::Reader::ExcelXML/spaces_are_empty> for more information.
1385              
1386             B<Accepts:> nothing
1387              
1388             B<Returns:> see the attribute L<Spreadsheet::Reader::ExcelXML/group_return_type> for
1389             details on what is returned
1390              
1391             =back
1392              
1393             =head3 fetchrow_arrayref( $row )
1394              
1395             =over
1396              
1397             B<Definition:> In an homage to L<DBI> I included this function to return an array ref of
1398             the cells or values in the requested $row. If no row is requested this returns the 'next'
1399             row. In the array ref any empty cell will show as 'undef'.
1400              
1401             B<Accepts:> undef = next|$row = a row integer indicating the desired row See the attribute
1402             L<Spreadsheet::Reader::ExcelXML/count_from_zero> to understand which row is returned for $row.
1403              
1404             B<Returns:> an array ref of all possible column positions in that row with data filled in
1405             per the attribute L<Spreadsheet::Reader::ExcelXML/group_return_type>.
1406              
1407             =back
1408              
1409             =head3 fetchrow_array( $row )
1410              
1411             =over
1412              
1413             B<Definition:> This function is just like L<fetchrow_arrayref|/fetchrow_arrayref( $row )>
1414             except it returns an array instead of an array ref
1415              
1416             B<Accepts:> undef = next|$row = a row integer indicating the desired row. See the attribute
1417             L<Spreadsheet::Reader::ExcelXML/count_from_zero> to understand which row is returned for $row.
1418              
1419             B<Returns:> an array ref of all possible column positions in that row with data filled in
1420             per the attribute L<Spreadsheet::Reader::ExcelXML/group_return_type>.
1421              
1422             =back
1423              
1424             =head3 set_headers( @header_row_list, [ \&header_scrubber ] )
1425              
1426             =over
1427              
1428             B<Definition:> This function is used to set headers used in the function
1429             L<fetchrow_hashref|/fetchrow_hashref( $row )>. It accepts a list of row numbers that
1430             will be collated into a set of headers used to build the hashref for each row.
1431             The header rows are coallated in sequence with the first position taking precedence.
1432             The list is also used to set the lowest row of the headers in the table. All rows
1433             at that level and higher will be considered out of the table and will return undef
1434             while setting the error instance. If some of the header columns do not have values
1435             then the instance will auto generate unique headers for each empty header column to
1436             fill out the header ref. [ optionally: it is possible to pass a coderef at the end of
1437             the list to scrub the headers so they make some sense.] When the headers are loaded
1438             the top level information row of any pull for 'fetchrow_hashref' will be considered
1439             the first row after the header row. Any header build will not be tested again during
1440             fetchrow_hashref the content columns will be matched to the header columns by position.
1441             for example;
1442              
1443             my $scrubber = sub{
1444             my $input = $_[0];
1445             $input =~ s/\n//g if $input;
1446             $input =~ s/\s/_/g if $input;
1447             return $input;
1448             };
1449             $self->set_headers( 2, 1, $scrubber ); # Called internally as $new_value = $scrubber->( $old_value );
1450             # Returns/stores the headers set at row 2 and 1 with values from row 2 taking precedence
1451             # Then it scrubs the values by removing newlines and replacing spaces with underscores.
1452              
1453             B<Accepts:> a list of row numbers (modified as needed by the attribute state of
1454             L<Spreadsheet::Reader::ExcelXML/count_from_zero>) and an optional L<closure
1455             |http://www.perl.com/pub/2002/05/29/closure.html>. See the attribute
1456             L<Spreadsheet::Reader::ExcelXML/count_from_zero> to understand which rows are
1457             used when the @header_row_list is called.
1458              
1459             B<Returns:> an array ref of the built headers for review.
1460              
1461             =back
1462              
1463             =head3 get_last_header_row
1464              
1465             =over
1466              
1467             B<Definition:> This returns the final row of headers in the sheet. The return value is
1468             in the context of the attribute L<Spreadsheet::Reader::ExcelXML/count_from_zero>. It is
1469             determined by the function 'set_headers'.
1470              
1471             B<Accepts:> nothing
1472              
1473             B<Returns:> the last (largest) row number used in the coallated header.
1474              
1475             =back
1476              
1477             =head3 header_row_set
1478              
1479             =over
1480              
1481             B<Definition:> This predicate will indicate if any header build has occured from the
1482             method 'set_headers'.
1483              
1484             B<Accepts:> nothing
1485              
1486             B<Returns:> Boolean on off for set headers (1 = headers are set)
1487              
1488             =back
1489              
1490             =head3 fetchrow_hashref( $row )
1491              
1492             =over
1493              
1494             B<Definition:> This function is used to return a hashref representing the data in the
1495             specified row. If no $row value is passed it will return the 'next' row of data. A call
1496             to this function without L<setting|/set_headers( @header_row_list )> the headers first
1497             will return 'undef' and set the error instance.
1498              
1499             B<Accepts:> a target $row number for return values or undef meaning 'next' See the
1500             attribute L<Spreadsheet::Reader::ExcelXML/count_from_zero> to understand which rows
1501             are targeted by $row.
1502              
1503             B<Returns:> a hash ref of the values for that row. This function ignores the attribute
1504             L<group_return_type|Spreadsheet::Reader::ExcelXML/group_return_type> when it is
1505             set to 'instance' and returns 'value's instead. See also the attributes
1506             L<min_header_col|/min_header_col> and L<max_header_col|/max_header_col> to pare the
1507             start and end columns of the returned hash ref.
1508              
1509             =back
1510              
1511             =head3 set_custom_formats( $format_ref )
1512              
1513             =over
1514              
1515             B<Definition:> This package will generate value conversions that generally match the
1516             numerical conversions set in the Excel spreadsheet. However, it may be that you want
1517             to convert the unformatted values for certain cells, rows, or columns in some user
1518             defined way. The simplest way to do this is by storing an
1519             L<Excel custom number format string
1520             |https://support.office.com/en-au/article/Create-or-delete-a-custom-number-format-78f2a361-936b-4c03-8772-09fab54be7f4>
1521             in this instance using 'set_custom_formats' against either a CellID, a Row Number, or a
1522             Column letter. As an example you could say;
1523              
1524             $worksheet->set_custom_formats( {
1525             A => '# ?/?',
1526             } );
1527              
1528             And any subsequent call for a $cell->value from column 'A' will attempt to convert the
1529             raw xml contents of that cell to an integer and fraction combination with one position
1530             in the denominator or less (an integer only). If the cell is text then it will act as
1531             a pass-through. Where there is overlap between the formats the instance will select
1532             one based on the following priority; cellID, column letter, row number.
1533              
1534             For the truly adventurous you can build an object instance that has the two following
1535             methods; 'assert_coerce' and 'display_name'. Then add it to the attribute as above.
1536              
1537             =over
1538              
1539             B<A Complicated Example:> Building a converter on the fly from L<Type::Tiny
1540             |Type::Tiny::Manual::Libraries> (or use L<MooseX::Types>)
1541              
1542             use DateTimeX::Format::Excel;
1543             use DateTime::Format::Flexible;
1544             use Type::Coercion;
1545             use Type::Tiny;
1546             my @args_list = ( system_type => 'apple_excel' );
1547             my $num_converter = DateTimeX::Format::Excel->new( @args_list );
1548              
1549             # build conversion subroutines (number and strings to DateTime objects)
1550             my $string_via = sub{
1551             my $str = $_[0];
1552             return DateTime::Format::Flexible->parse_datetime( $str );
1553             };
1554             my $num_via = sub{
1555             my $num = $_[0];
1556             return $num_converter->parse_datetime( $num );
1557             };
1558              
1559             # Combine conversion subroutines into a coercion object!
1560             # (Note numbers are attempted first)
1561             my $date_time_from_value = Type::Coercion->new(
1562             type_coercion_map => [ Num, $num_via, Str, $string_via, ],
1563             );
1564              
1565             # Install the coercion in a type that ensures it passes through a DateTime check
1566             $date_time_type = Type::Tiny->new(
1567             name => 'Custom_date_type',
1568             constraint => sub{ ref($_) eq 'DateTime' },
1569             coercion => $date_time_from_value,
1570             );
1571              
1572             # Chained coercions! to handle first the $date_time_from_value coercion
1573             # and then build a specific date string output
1574             $string_type = Type::Tiny->new(
1575             name => 'YYYYMMDD',
1576             constraint => sub{
1577             !$_ or (
1578             $_ =~ /^\d{4}\-(\d{2})-(\d{2})$/ and
1579             $1 > 0 and $1 < 13 and $2 > 0 and $2 < 32
1580             )
1581             },
1582             coercion => Type::Coercion->new(
1583             type_coercion_map =>[
1584             $date_time_type->coercibles, sub{
1585             my $tmp = $date_time_type->coerce( $_ );
1586             $tmp->format_cldr( 'yyyy-MM-dd' )
1587             },
1588             ],
1589             ), );
1590              
1591             # Then set the coercions in the worksheet for targeted positions
1592             $worksheet->set_custom_formats( {
1593             E10 => $date_time_type,
1594             10 => $string_type,
1595             D14 => $string_type,
1596             } );
1597              
1598             =back
1599              
1600             B<Accepts:> a set of $key => $value pairs where the $key can either be a
1601             row number, a column letter, or a cell ID. The $value must either be a
1602             string that complies with excel custom number formatting conventions or
1603             an object instance with two methods 'display_name' and 'assert_coerce'.
1604              
1605             B<Returns:> nothing
1606              
1607             =back
1608              
1609             =head3 has_custom_format( $key )
1610              
1611             =over
1612              
1613             B<Definition:> This is a test to see if a custom format is stored for $key.
1614              
1615             B<Accepts:> $key
1616              
1617             B<Returns:> a boolean where 1 = formats exist
1618              
1619             =back
1620              
1621             =head3 get_custom_format( $key )
1622              
1623             =over
1624              
1625             B<Definition:> This will retrieve the $value_ref of the custom format stored
1626             against the given $key. For formats saved as Excel custom format strings this
1627             will return a package built object instance that performs the conversion
1628             indicated by the custom format string.
1629              
1630             B<Accepts:> $key
1631              
1632             B<Returns:> a $value_ref for the stored format object
1633              
1634             =back
1635              
1636             =head3 get_custom_formats
1637              
1638             =over
1639              
1640             B<Definition:> This returns a hashref of all stored custom formats
1641              
1642             B<Accepts:> nothing
1643              
1644             B<Returns:> a hashref of all custom formats
1645              
1646             =back
1647              
1648             =head2 Attributes
1649              
1650             These are attributes of the built instance. Most of these are provided as metadata for
1651             the specific parsed sheet and should be provided by the object that builds the worksheet
1652             instance. (The workbook) However, a few can be set after worksheet creation to affect the
1653             data retreival results. These attributes are presented first. The end of the list is the
1654             attribute that stores a reference to the workbook. Follow the link in thats attribute
1655             documentation to learn a tricksy way to adjust several workbook level settings with the
1656             worksheet instance.
1657              
1658             =head3 min_header_col
1659              
1660             =over
1661              
1662             B<Definition:> This attribute affects the hashref that is returned in the method
1663             L<fetchrow_hashref|/fetchrow_hashref( $row )>. This attribute tells fetchrow_hashref
1664             what column to use to start the hash ref build. This attribute (input)
1665             does respond to the attribute L<Spreadsheet::Reader::ExcelXML/count_from_zero>.
1666              
1667             B<Default:> undef (which is equivalent to the minimum column of the sheet)
1668              
1669             B<Range:> The minimum column of the sheet to or less than the
1670             L<max_header_col|/max_header_col>
1671              
1672             B<attribute methods> Methods provided to adjust this attribute
1673              
1674             =over
1675              
1676             B<get_min_header_col>
1677              
1678             =over
1679              
1680             B<Definition:> returns the value stored in the attribute
1681              
1682             =back
1683              
1684             B<set_min_header_col>
1685              
1686             =over
1687              
1688             B<Definition:> Sets a new value for the attribute
1689              
1690             =back
1691              
1692             B<has_min_header_col>
1693              
1694             =over
1695              
1696             B<Definition:> Indicates if the attribute has a stored value
1697              
1698             =back
1699              
1700             =back
1701              
1702             =back
1703              
1704             =head3 max_header_col
1705              
1706             =over
1707              
1708             B<Definition:> This attribute affects the hashref that is returned in the method
1709             L<fetchrow_hashref|/fetchrow_hashref( $row )>. This attribute tells fetchrow_hashref
1710             what column to use to end the hash ref build. This attribute (input) does respond to
1711             the attribute L<Spreadsheet::Reader::ExcelXML/count_from_zero>.
1712              
1713             B<Default:> undef (equal to the maximum column of the sheet)
1714              
1715             B<Range:> The maximum column of the sheet to or less than the
1716             L<min_header_col|/min_header_col>
1717              
1718             B<attribute methods> Methods provided to adjust this attribute
1719              
1720             =over
1721              
1722             B<get_max_header_col>
1723              
1724             =over
1725              
1726             B<Definition:> returns the value stored in the attribute
1727              
1728             =back
1729              
1730             B<set_max_header_col>
1731              
1732             =over
1733              
1734             B<Definition:> Sets a new value for the attribute
1735              
1736             =back
1737              
1738             B<has_max_header_col>
1739              
1740             =over
1741              
1742             B<Definition:> Indicates if the attribute has a stored value
1743              
1744             =back
1745              
1746             =back
1747              
1748             =back
1749              
1750             =head3 file
1751              
1752             =over
1753              
1754             B<Definition:> This attribute holds the file handle for the file being read. If
1755             the full file name and path is passed to the attribute the class will coerce that
1756             into an L<IO::File> file handle.
1757              
1758             B<Default:> no default - this must be provided to read a file
1759              
1760             B<Required:> yes
1761              
1762             B<Range:> any unencrypted xml file name and path or IO::File file handle set to
1763             read.
1764              
1765             B<attribute methods> Methods provided to adjust this attribute
1766              
1767             =over
1768              
1769             B<set_file>
1770              
1771             =over
1772              
1773             B<Definition:> change the file value in the attribute (this will reboot
1774             the file instance and should lock the file)
1775              
1776             =back
1777              
1778             B<get_file>
1779              
1780             =over
1781              
1782             B<Definition:> Returns the file handle of the file even if a file name
1783             was passed
1784              
1785             =back
1786              
1787             B<has_file>
1788              
1789             =over
1790              
1791             B<Definition:> this is used to see if the file loaded correctly.
1792              
1793             =back
1794              
1795             B<clear_file>
1796              
1797             =over
1798              
1799             B<Definition:> this clears (and unlocks) the file handle
1800              
1801             =back
1802              
1803             =back
1804              
1805             B<Delegated Methods>
1806              
1807             =over
1808              
1809             L<close|IO::Handle/$io-E<gt>close>
1810              
1811             =over
1812              
1813             closes the file handle
1814              
1815             =back
1816              
1817             L<seek|IO::Seekable/$io-E<gt>seek ( POS, WHENCE )>
1818              
1819             =over
1820              
1821             allows seek commands to be passed to the file handle
1822              
1823             =back
1824              
1825             L<getline|IO::Handle/$io-E<gt>getline>
1826              
1827             =over
1828              
1829             returns the next line of the file handle with '<' set as the
1830             L<input_record_separator ($E<sol>)|http://perldoc.perl.org/perlvar.html>
1831              
1832             =back
1833              
1834             =back
1835              
1836             =back
1837              
1838             =head3 sheet_type
1839              
1840             =over
1841              
1842             B<Definition:> This is the sheet 'type' which will always be 'worksheet'
1843             (as opposed to chartsheet)
1844              
1845             B<attribute methods> Methods provided to adjust this attribute
1846              
1847             =over
1848              
1849             B<get_sheet_type>
1850              
1851             =over
1852              
1853             B<Definition:> returns the value stored in the attribute (worsheet)
1854              
1855             =back
1856              
1857             =back
1858              
1859             =back
1860              
1861             =head3 sheet_rel_id
1862              
1863             =over
1864              
1865             B<Definition:> This is the relId of the sheet listed in the XML of the .xlsx file.
1866             You probably don't care and you should never set this value.
1867              
1868             B<attribute methods> Methods provided to adjust this attribute
1869              
1870             =over
1871              
1872             B<rel_id>
1873              
1874             =over
1875              
1876             B<Definition:> returns the value stored in the attribute
1877              
1878             =back
1879              
1880             =back
1881              
1882             =back
1883              
1884             =head3 sheet_id
1885              
1886             =over
1887              
1888             B<Definition:> This is the Id of the sheet listed in the XML of the .xlsx file.
1889             I beleive this to be the number used in vbscript to reference the sheet. You
1890             should never set this value.
1891              
1892             B<attribute methods> Methods provided to adjust this attribute
1893              
1894             =over
1895              
1896             B<sheet_id>
1897              
1898             =over
1899              
1900             B<Definition:> returns the value stored in the attribute
1901              
1902             =back
1903              
1904             =back
1905              
1906             =back
1907              
1908             =head3 sheet_position
1909              
1910             =over
1911              
1912             B<Definition:> This is the visual sheet position in the .xlsx file.
1913             You should never set this value.
1914              
1915             B<attribute methods> Methods provided to adjust this attribute
1916              
1917             =over
1918              
1919             B<position>
1920              
1921             =over
1922              
1923             B<Definition:> returns the value stored in the attribute
1924              
1925             =back
1926              
1927             =back
1928              
1929             =back
1930              
1931             =head3 sheet_name
1932              
1933             =over
1934              
1935             B<Definition:> This is the visual sheet name in the .xlsx file
1936             on the tab. You should never set this value.
1937              
1938             B<attribute methods> Methods provided to adjust this attribute
1939              
1940             =over
1941              
1942             B<get_name>
1943              
1944             =over
1945              
1946             B<Definition:> returns the value stored in the attribute
1947              
1948             =back
1949              
1950             =back
1951              
1952             =back
1953              
1954             =head3 workbook_instance
1955              
1956             =over
1957              
1958             B<Definition:> This attribute holds a reference back to the workbook instance so that
1959             the worksheet has access to the global settings managed there. As a consequence many
1960             of the workbook methods are be exposed here. This includes some setter methods for
1961             workbook attributes. I<Beware that setting or adjusting the workbook level attributes
1962             with methods here will be universal and affect other worksheets. So don't forget to
1963             return the old value if you want the old behavour after you are done.> If that
1964             doesn't make sense then don't use these methods. (Nothing to see here! Move along.)
1965              
1966             B<Default:> a Spreadsheet::Reader::ExcelXML::Workbook instance
1967              
1968             B<attribute methods> Methods used to change the workbook instance
1969              
1970             =over
1971              
1972             B<set_workbook_inst>
1973              
1974             =over
1975              
1976             B<Definition:> Sets the attribute with a new workbook instance
1977              
1978             =back
1979              
1980             =back
1981              
1982             B<Delegated attribute methods> Since this list can float please L<follow the link
1983             |Spreadsheet::Reader::ExcelXML::XMLReader/Delegated Methods (required)>
1984             to the documented delegations in that file.
1985              
1986             =back
1987              
1988             =head1 SUPPORT
1989              
1990             =over
1991              
1992             L<github Spreadsheet::Reader::ExcelXML/issues
1993             |https://github.com/jandrew/p5-spreadsheet-reader-excelxml/issues>
1994              
1995             =back
1996              
1997             =head1 TODO
1998              
1999             =over
2000              
2001             B<1.> Nothing L<Yet|https://github.com/jandrew/p5-spreadsheet-reader-excelxml/issues>!
2002              
2003             =back
2004              
2005             =head1 AUTHOR
2006              
2007             =over
2008              
2009             =item Jed Lund
2010              
2011             =item jandrew@cpan.org
2012              
2013             =back
2014              
2015             =head1 COPYRIGHT
2016              
2017             This program is free software; you can redistribute
2018             it and/or modify it under the same terms as Perl itself.
2019              
2020             The full text of the license can be found in the
2021             LICENSE file included with this module.
2022              
2023             This software is copyrighted (c) 2016 by Jed Lund
2024              
2025             =head1 DEPENDENCIES
2026              
2027             =over
2028              
2029             L<Spreadsheet::Reader::ExcelXML> - the package
2030              
2031             =back
2032              
2033             =head1 SEE ALSO
2034              
2035             =over
2036              
2037             L<Spreadsheet::Read> - generic Spreadsheet reader
2038              
2039             L<Spreadsheet::ParseExcel> - Excel binary version 2003 and earlier (.xls files)
2040              
2041             L<Spreadsheet::XLSX> - Excel version 2007 and later
2042              
2043             L<Spreadsheet::ParseXLSX> - Excel version 2007 and later
2044              
2045             L<Log::Shiras|https://github.com/jandrew/Log-Shiras>
2046              
2047             =over
2048              
2049             All lines in this package that use Log::Shiras are commented out
2050              
2051             =back
2052              
2053             =back
2054              
2055             =cut
2056              
2057             #########1#########2 main pod documentation end 5#########6#########7#########8#########9