File Coverage

lib/Spreadsheet/Reader/ExcelXML/XMLReader/NamedWorksheet.pm
Criterion Covered Total %
statement 241 250 96.4
branch 104 118 88.1
condition 70 99 70.7
subroutine 15 15 100.0
pod 3 3 100.0
total 433 485 89.2


line stmt bran cond sub pod time code
1             package Spreadsheet::Reader::ExcelXML::XMLReader::NamedWorksheet;
2             our $AUTHORITY = 'cpan:JANDREW';
3 5     5   14346 use version; our $VERSION = version->declare('v0.16.8');
  5         10  
  5         31  
4             ###LogSD warn "You uncovered internal logging statements for Spreadsheet::Reader::ExcelXML::XMLReader::NamedWorksheet-$VERSION";
5              
6 5     5   551 use 5.010;
  5         14  
7 5     5   23 use Moose::Role;
  5         5  
  5         37  
8             requires qw(
9             current_named_node current_node_parsed squash_node
10             advance_element_position good_load start_the_file_over
11             _build_cell_label get_epoch_year parse_element
12             spreading_merged_values should_skip_hidden are_spaces_empty
13             get_empty_return_type get_values_only
14             );
15 5     5   18130 use Clone 'clone';
  5         8  
  5         249  
16 5     5   20 use Carp qw( confess );
  5         6  
  5         224  
17 5         44 use Types::Standard qw(
18             Bool Int is_HashRef ArrayRef
19             HashRef
20 5     5   19 );
  5         7  
21 5     5   3361 use MooseX::ShortCut::BuildInstance qw ( build_instance should_re_use_classes );
  5         8  
  5         50  
22 5     5   3085 use DateTime::Format::Flexible;
  5         792818  
  5         66  
23 5     5   1566 use DateTimeX::Format::Excel;
  5         145575  
  5         342  
24             should_re_use_classes( 1 );
25 5     5   27 use lib '../../../../lib';
  5         7  
  5         39  
26             ###LogSD use Log::Shiras::Telephone;
27             #########1 Dispatch Tables & Package Variables 5#########6#########7#########8#########9
28              
29             my $format_translations = {
30             width => 'ss:Width',
31             customWidth => 'ss:CustomWidth',
32             bestFit => 'ss:AutoFitWidth',
33             hidden => 'ss:Hidden',
34             mergeAcross => 'ss:MergeAcross',
35             mergeDown => 'ss:MergeDown',
36             r => 'ss:Index',
37             cell_col => 'ss:Index',
38             ht => 'ss:Height',
39             cell_type => 'ss:Type',
40             s => 'ss:StyleID',
41             cell_xml_value => 'raw_text',
42             cell_formula => 'ss:Formula',
43             };
44              
45             my $rich_text_translations = {
46             'html:Color' => 'rgb',
47             'html:Size' => 'sz',
48             };
49              
50             my $width_translation = 9.5703125/50.25;# Translation from 2003 xml file width to 2007+ width
51              
52             #########1 Public Attributes 3#########4#########5#########6#########7#########8#########9
53              
54             has is_hidden =>(
55             isa => Bool,
56             reader => 'is_sheet_hidden',
57             writer => '_set_sheet_hidden',
58             default => 0,
59             );
60              
61             #########1 Public Methods 3#########4#########5#########6#########7#########8#########9
62              
63             sub advance_row_position{
64 71     71 1 100 my( $self, $increment ) = @_;#, $new_file, $old_file
65 71   100     259 $increment //= 1;
66             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
67             ###LogSD $self->get_all_space . '::NamedWorksheet::advance_row_position', );
68             ###LogSD $phone->talk( level => 'info', message => [
69             ###LogSD "Moving row forward -$increment- times", ] );
70 71         66 my $new_ref;
71 71         133 for my $x ( 1 .. $increment ){
72 80         244 my( $result, $node_name, $node_level, $result_ref ) =
73             $self->advance_element_position( 'Row' );
74             ###LogSD $phone->talk( level => 'info', message => [
75             ###LogSD "Advanced to 'Row' increment -$x- time(s) arriving at node -" .
76             ###LogSD "$node_name- with result: " . ($result//'fail'), ] );
77 80 100       187 last if !$result;
78 73         88 $new_ref = undef;
79 73         203 my $row_node = $self->current_node_parsed;
80             ###LogSD $phone->talk( level => 'debug', message => [
81             ###LogSD "current node is:", $row_node] );
82             map{
83 73 100       98 if( defined $row_node->{Row}->{$format_translations->{$_}} ){
  219         572  
84 55         195 $new_ref->{$_} = $row_node->{Row}->{$format_translations->{$_}}
85             }
86             } qw( r hidden ht );
87             ###LogSD $phone->talk( level => 'debug', message => [
88             ###LogSD "converted result is:", $new_ref] );
89 73   66     2171 $self->_set_current_row_number( $new_ref->{r}//($self->_get_current_row_number + 1) );
90 73   66     1408 $new_ref->{r} //= $self->_get_current_row_number;
91             ###LogSD $phone->talk( level => 'info', message => [
92             ###LogSD "augmented result is:", $new_ref] );
93 73         2041 $self->_set_custom_row_data( $new_ref->{r} => $new_ref );# Should this be tied into cache_positions?
94             }
95              
96 71         191 return $new_ref;
97             }
98              
99             sub build_row_data{
100 64     64 1 73 my( $self, ) = @_;# $row_ref, $row_position
101             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
102             ###LogSD $self->get_all_space . '::NamedWorksheet::build_row_data', );
103             ###LogSD $phone->talk( level => 'debug', message => [
104             ###LogSD "Building out the current row node", ] );
105 64         165 my $full_row_ref = $self->squash_node( $self->parse_element );
106             ###LogSD $phone->talk( level => 'trace', message =>[
107             ###LogSD "Full row parsed to:", $full_row_ref, ] );
108              
109             # Check EOF
110 64 50       169 if( !ref( $full_row_ref ) ){
111             ###LogSD $phone->talk( level => 'trace', message =>[
112             ###LogSD "Probably found an end of file flag: $full_row_ref", ] );
113 0         0 return $full_row_ref;
114             }
115              
116             # Confirm row list
117 64 100       140 if( exists $full_row_ref->{Cell} ){
118 20         49 my $alt_row->{list} = [ $full_row_ref->{Cell} ];
119 20         26 delete $full_row_ref->{Cell};
120 20         28 $alt_row->{attributes} = $full_row_ref;
121 20         19 $full_row_ref = $alt_row;
122             }
123 64         63 my $new_ref;
124             map{
125 64 100       80 if( defined $full_row_ref->{attributes}->{$format_translations->{$_}} ){
  192         480  
126 45         77 $full_row_ref->{attributes}->{$_} = $full_row_ref->{attributes}->{$format_translations->{$_}};
127 45         127 delete $full_row_ref->{attributes}->{$format_translations->{$_}};
128             }
129             } qw( r hidden ht );
130 64         1917 $new_ref->{row_number} = $self->_get_current_row_number;
131 64         94 $new_ref->{row_formats} = $full_row_ref->{attributes};
132             ###LogSD $phone->talk( level => 'trace', message =>[
133             ###LogSD "updated Full row:", $full_row_ref,
134             ###LogSD "New row ref initialized as:", $new_ref ] );
135              
136             # set spans value
137 64   50     1469 $new_ref->{row_span} = [($self->_min_col//1),($self->_max_col//1)];
      100        
138             ###LogSD $phone->talk( level => 'trace', message =>[
139             ###LogSD "Updated new ref:", $new_ref ] );
140              
141             # Parse the cells for position and range
142 64         97 my $column_to_cell_translations = [];
143 64         70 my $last_value_column;
144             my $alt_ref;
145 64         79 my $current_column = 0;
146 64         69 for my $cell ( @{$full_row_ref->{list}} ){
  64         156  
147             ###LogSD $phone->talk( level => 'debug', message =>[
148             ###LogSD "Processing cell:", $cell] );
149             map{
150 186 100       224 if( defined $cell->{$format_translations->{$_}} ){
  1116         2056  
151 166         242 $cell->{$_} = $cell->{$format_translations->{$_}};
152 166         407 delete $cell->{$format_translations->{$_}};
153             }
154             } qw( cell_col hidden s mergeAcross mergeDown cell_formula );
155 186 100       299 if( exists $cell->{Data} ){# Handle regular values
156             map{
157 176 100       175 if( defined $cell->{Data}->{$format_translations->{$_}} ){
  352         623  
158 342         859 $cell->{$_} = $cell->{Data}->{$format_translations->{$_}};
159             }
160             } qw( cell_type cell_xml_value );
161 176         242 delete $cell->{Data};
162             }else{# Handle rich text
163 10         38 @$cell{ qw( cell_xml_value rich_text cell_type ) } = $self->_process_data_element( $cell->{'ss:Data'} );
164 10         13 delete $cell->{'ss:Data'};
165             }
166 186 100       282 if( defined $cell->{cell_col} ){
167 41         55 $current_column = $cell->{cell_col};
168             }else{
169 145         165 $cell->{cell_col} = ++$current_column;
170             }
171 186         4755 $cell->{cell_row} = $self->_get_current_row_number;
172 186         540 $cell->{r} = $self->_build_cell_label( @$cell{qw(cell_col cell_row)} );
173             ###LogSD $phone->talk( level => 'debug', message =>[
174             ###LogSD "Updated cell:", $cell ] );
175              
176             # Handle weird empty Number cells treated as 0
177 186 100 66     936 if( exists $cell->{cell_type} and $cell->{cell_type} eq 'Number' and
      66        
      66        
178             ( !$cell->{cell_xml_value} or $cell->{cell_xml_value} eq '' ) ){
179             ###LogSD $phone->talk( level => 'debug', message =>[
180             ###LogSD "Identified wierd Excel 2003 xml file format where empty number cells are represented as 0" ] );
181 3         5 $cell->{cell_unformatted} = 0;
182             }
183             ###LogSD $phone->talk( level => 'debug', message =>[
184             ###LogSD "Updated cell:", $cell ] );
185              
186             # resolve cell_xml_value, and rich text
187             # can't delay this because the information is required for 'empty_is_end'
188             # No v-node to collect here. I don't think shared strings works well or at all in this format
189 186 100 66     548 if( $self->are_spaces_empty and $cell->{cell_xml_value} and $cell->{cell_xml_value} =~ /^\s+$/ ){
      100        
190             ###LogSD $phone->talk( level => 'debug', message =>[
191             ###LogSD "Clearing spaces only xml value: |$cell->{cell_xml_value}|" ] );
192 5         109 delete $cell->{cell_xml_value};
193             }
194 186 100 33     1241 if( $self->get_empty_return_type eq 'empty_string' ){
    100 66        
195 40 100 66     107 $cell->{cell_xml_value} = '' if !exists $cell->{cell_xml_value} or !defined $cell->{cell_xml_value};
196             }elsif( !defined $cell->{cell_xml_value} or
197             ($cell->{cell_xml_value} and length( $cell->{cell_xml_value} ) == 0) ){
198 15         124 delete $cell->{cell_xml_value};
199             }
200             ###LogSD $phone->talk( level => 'debug', message =>[
201             ###LogSD "Updated cell:", $cell] );
202              
203             # Handle DateTime cell_type(s)
204 186 100       1003 if( $cell->{cell_type} eq 'DateTime'){
205             ###LogSD $phone->talk( level => 'debug', message =>[
206             ###LogSD "Transforming:", $cell->{cell_xml_value} ] );
207 25         174 my $dt = DateTime::Format::Flexible->parse_datetime( $cell->{cell_xml_value} );
208 25         146628 $cell->{cell_unformatted} = $self->_format_datetime( $dt );
209 25         8257 $cell->{cell_type} = 'Date';
210             }
211             ###LogSD $phone->talk( level => 'debug', message =>[
212             ###LogSD "Updated cell:", $cell] );
213              
214             # Store merge range if any
215 186 100 66     524 if( exists $cell->{mergeAcross} or exists $cell->{mergeDown} ){
216 8         14 my $final_column = $cell->{cell_col};
217 8 50       30 $final_column += $cell->{mergeAcross} if exists $cell->{mergeAcross};
218 8         14 my $final_row = $cell->{cell_row};
219 8 50       19 $final_row += $cell->{mergeDown} if exists $cell->{mergeDown};
220 8         27 my $merge_range = $cell->{r} . ':' . $self->_build_cell_label( $final_column, $final_row );
221 8         15 $cell->{cell_merge} = $merge_range;
222 8         15 delete $cell->{mergeAcross};
223 8         9 delete $cell->{mergeDown};
224             ###LogSD $phone->talk( level => 'debug', message => [
225             ###LogSD "Updated cell merge range: $merge_range", ] );
226 8         24 for my $row ( $cell->{cell_row} .. $final_row ){
227 8         238 my $row_merge_ref = $self->_get_row_merge_map( $row );
228             ###LogSD $phone->talk( level => 'debug', message => [
229             ###LogSD "Building on row merge map:", $row_merge_ref ] );
230 8         26 for my $col ( $cell->{cell_col} .. $final_column ){
231 16         26 $row_merge_ref->[$col] = $merge_range;
232             }
233 8         251 $self->_set_row_merge_map( $row => $row_merge_ref );
234             }
235             ###LogSD $phone->talk( level => 'debug', message => [
236             ###LogSD "Updated merge map:", $self->get_merge_map ] );
237 8         241 $self->_set_merged_value( $merge_range => $cell );
238             }
239             ###LogSD $phone->talk( level => 'debug', message =>[
240             ###LogSD "Updated cell:", $cell] );
241              
242             # Handle formula, position translations, and last value
243 186         459 $last_value_column = $cell->{cell_col};
244 186         222 push @$alt_ref, $cell;
245 186         366 $column_to_cell_translations->[$cell->{cell_col}] = $#$alt_ref;
246             ###LogSD $phone->talk( level => 'debug', message => [
247             ###LogSD 'Saving cell:', $cell, $alt_ref,
248             ###LogSD "..with column to cell translations:", $column_to_cell_translations,
249             ###LogSD "..and last value column: $last_value_column", ] );
250             }
251              
252             # Scrub merge cells, column formats, and then empty values
253             my $max_column = $new_ref->{row_span}->[1] < $last_value_column ?
254 64 100       192 $last_value_column : $new_ref->{row_span}->[1];
255 64         76 my( $cell_stack, $position_stack );
256 64         1852 my $row_merge_range = $self->_get_row_merge_map( $new_ref->{row_number} );
257             ###LogSD $phone->talk( level => 'debug', message => [
258             ###LogSD "Full row merge settings:", $row_merge_range ] );
259 64         61 my $final_column_translations;
260 64         124 SCRUBINGCELLSTACK: for my $col ( 1 .. $max_column ){
261             ###LogSD $phone->talk( level => 'debug', message => [
262             ###LogSD "Processing column: $col", ,
263             ###LogSD (defined $column_to_cell_translations->[$col] ? $alt_ref->[$column_to_cell_translations->[$col]] : undef) ] );
264 367         247 my $new_cell;
265              
266             # Resolve additional merge info
267 367 100 66     565 if( $row_merge_range and $row_merge_range->[$col] ){
268             ###LogSD $phone->talk( level => 'debug', message => [
269             ###LogSD "Column -$col- is part of a merge range" ] );
270              
271             # Handle primary (merged) position sharing
272 16 100 66     62 if( $self->spreading_merged_values and
      100        
273             ( !defined $column_to_cell_translations->[$col] or
274             !exists $alt_ref->[$column_to_cell_translations->[$col]]->{cell_merge} ) ){ # Test for the other (not primary) merged cells
275 2         80 $new_cell = $self->_get_merged_value( $row_merge_range->[$col] );
276 2         51 my $row_delta = $new_cell->{cell_row} - $self->_get_current_row_number;
277 2         48 $new_cell->{cell_row} = $self->_get_current_row_number;
278 2         3 my $column_delta = $new_cell->{cell_col} - $col;
279 2         2 $new_cell->{cell_col} = $col;
280 2         7 $new_cell->{r} = $self->_build_cell_label( $new_cell->{cell_col}, $new_cell->{cell_row} );
281 2 100 66     13 if( exists $new_cell->{cell_formula} and $new_cell->{cell_formula} =~ /R\[?(-?\d*)\]?C\[?(-?\d*)\]?(.*)/ ){
282             ###LogSD $phone->talk( level => 'debug', message => [
283             ###LogSD "Adjusting the RC style formula: ", $new_cell->{cell_formula},
284             ###LogSD "..with row_delta: $row_delta", "..and column_delta: $column_delta" ] );
285 1         2 my $new_formula;
286 1         4 my @match_list = split /(R)/, $new_cell->{cell_formula};
287 1         3 for my $section ( @match_list ){
288 3 100       8 if( $section =~ /\[?(-?\d*)\]?C\[?(-?\d*)\]?(.*)/g ){
289 1   50     5 my $row_offset = ($1//0);
290 1   50     3 my $column_offset = ($2//0);
291 1   50     3 my $end_string = ($3//'');
292             ###LogSD $phone->talk( level => 'debug', message => [
293             ###LogSD "Managing row offset -$row_offset- column offset -$column_offset- and end string: $end_string" ] );
294 1         4 $new_formula .= '[' . ($row_offset + $row_delta) . ']' . 'C[' . ($column_offset + $column_delta) . ']' . $end_string;
295             ###LogSD $phone->talk( level => 'debug', message => [
296             ###LogSD "Added row - column offsets with result: $new_formula" ] );
297             }else{
298 2         3 $new_formula .= $section;
299             ###LogSD $phone->talk( level => 'debug', message => [
300             ###LogSD "Added formula string with result: $new_formula" ] );
301             }
302             }
303 1         3 $new_cell->{cell_formula} = $new_formula;
304             ###LogSD $phone->talk( level => 'debug', message => [
305             ###LogSD "Updated formula: ", $new_cell->{cell_formula} ] );
306             }
307             ###LogSD $phone->talk( level => 'debug', message => [
308             ###LogSD "New cell now has: ", $new_cell ] );
309             }
310             }
311              
312             # Handle formats (especially from the column) including hidden - more work needed here!
313 367 100 66     1375 if( $new_cell or (defined $column_to_cell_translations->[$col] and
      66        
314             $alt_ref->[$column_to_cell_translations->[$col]] ) ){
315             $new_cell->{cell_hidden} =
316             $self->is_sheet_hidden ? 'sheet' :
317             ( $self->get_custom_column_data( $col ) and
318             exists $self->get_custom_column_data( $col )->{hidden} and
319             $self->get_custom_column_data( $col )->{hidden} ) ? 'column' :
320 188 100 66     4351 $full_row_ref->{attributes}->{hidden} ? 'row' : undef ;
    100          
    100          
321 188 100       376 delete $new_cell->{cell_hidden} if !$new_cell->{cell_hidden};
322             ###LogSD $phone->talk( level => 'debug', message => [
323             ###LogSD "updated new cell now has: ", $new_cell ] );
324             }
325              
326             # Load in remaining data
327             ###LogSD $phone->talk( level => 'debug', message => [
328             ###LogSD "Possibly loading additional cell data: ", defined $column_to_cell_translations->[$col] ? $alt_ref->[$column_to_cell_translations->[$col]] : $column_to_cell_translations->[$col] ] );
329 367 100 66     987 if( defined $column_to_cell_translations->[$col] and $alt_ref->[$column_to_cell_translations->[$col]] ){
330             ###LogSD $phone->talk( level => 'debug', message => [
331             ###LogSD "Mapping file column -$col- from position: $column_to_cell_translations->[$col]", ] );
332 186 50       158 map{ $new_cell->{$_} = $alt_ref->[$column_to_cell_translations->[$col]]->{$_} if !exists $new_cell->{$_} } keys %{$alt_ref->[$column_to_cell_translations->[$col]]};
  1078         3081  
  186         484  
333             }
334             ###LogSD $phone->talk( level => 'debug', message =>[
335             ###LogSD "Updated new cell:", $new_cell ] );
336              
337             # Skip hidden
338             ###LogSD $phone->talk( level => 'debug', message =>[
339             ###LogSD "Should skip hidden: " . $self->should_skip_hidden,
340             ###LogSD "With hidden value exists: " . (($new_cell and exists $new_cell->{ cell_hidden }) ? $new_cell->{ cell_hidden } : 'undef'), $new_cell ] );
341 367 100 100     962 if( $self->should_skip_hidden and $new_cell and exists $new_cell->{cell_hidden} ){
      66        
342             ###LogSD $phone->talk( level => 'debug', message =>[
343             ###LogSD "Skipping a known hidden cell", $new_cell ] );
344 15         207 next SCRUBINGCELLSTACK;
345             }
346              
347             # Skip empty
348             ###LogSD $phone->talk( level => 'debug', message =>[
349             ###LogSD "Checking values only: " . $self->get_values_only,
350             ###LogSD "With xml value exists: " . exists $new_cell->{cell_xml_value}, ] );
351 352 100 100     1892 if( $self->get_values_only and $new_cell and !exists $new_cell->{cell_xml_value} ){
      100        
352             ###LogSD $phone->talk( level => 'debug', message =>[
353             ###LogSD "Skipping the empty cell", $new_cell ] );
354 1         15 next SCRUBINGCELLSTACK;
355             }
356              
357             # Handle cell type
358 351 50 66     1782 if( $new_cell and exists $new_cell->{cell_type} ){
359 172         302 $new_cell->{cell_type} =~ s/String/Text/;
360             }
361              
362             # Stack new data
363             ###LogSD $phone->talk( level => 'debug', message =>[
364             ###LogSD "Checking if anything was stored in new_cell:", $new_cell ] );
365 351 100 66     975 if( $new_cell and (keys %$new_cell ) > 0 ){
366 172         198 push @$cell_stack, $new_cell;
367 172         226 $position_stack->[$new_cell->{cell_col}] = $#$cell_stack;
368             ###LogSD $phone->talk( level => 'debug', message => [
369             ###LogSD 'Updated cell stack:', $cell_stack,
370             ###LogSD '..with position stack:', $position_stack ] );
371 172         243 $last_value_column = $new_cell->{cell_col};
372             }
373             }
374 64         109 $new_ref->{column_to_cell_translations} = $position_stack;
375              
376             # Handle full empty rows here
377             ###LogSD $phone->talk( level => 'debug', message => [
378             ###LogSD 'Updated cell stack:', $cell_stack,
379             ###LogSD '..with position stack:', $position_stack,
380             ###LogSD '..and last value column:', $last_value_column ] );
381 64 100       110 if( $cell_stack ){
382 57         78 $new_ref->{row_value_cells} = $cell_stack;
383             # Update max column as needed
384 57         86 $new_ref->{row_last_value_column} = $last_value_column;
385 57 100 66     1361 if( !$self->has_max_col or $last_value_column > $self->_max_col ){
386             ###LogSD $phone->talk( level => 'debug', message => [
387             ###LogSD "Updating max column with: $last_value_column", ] );
388 1         26 $self->_set_max_col( $last_value_column );
389             }
390             # Update span end
391 57 100       127 if( $new_ref->{row_span}->[1] < $last_value_column ){
392             ###LogSD $phone->talk( level => 'debug', message => [
393             ###LogSD "Updating span-end with: $last_value_column", ] );
394 1         2 $new_ref->{row_span}->[1] = $last_value_column;
395             }
396             # Update max row
397 57 100 100     1383 if( !$self->has_max_row or $new_ref->{row_number} > $self->_max_row ){
398             ###LogSD $phone->talk( level => 'debug', message => [
399             ###LogSD "Updating max row with: $new_ref->{row_number}", ] );
400 4         94 $self->_set_max_row( $new_ref->{row_number} );
401             }
402             # Update max column
403 57 50 33     1290 if( !$self->has_max_col or $new_ref->{row_span}->[1] > $self->_max_col ){
404             ###LogSD $phone->talk( level => 'debug', message => [
405             ###LogSD "Updating max column with: $new_ref->{row_span}->[1]", ] );
406 0         0 $self->_set_max_col( $new_ref->{row_span}->[1] );
407             }
408             }else{
409             ###LogSD $phone->talk( level => 'debug', message => [
410             ###LogSD 'No data available for this row', ] );
411 7         45 return undef;
412             }
413              
414              
415             ###LogSD $phone->talk( level => 'trace', message =>[
416             ###LogSD "Updated new ref:", $new_ref,] );
417 57         395 return $new_ref;
418             }
419              
420             sub load_unique_bits{
421 10     10 1 20 my( $self, ) = @_;#, $new_file, $old_file
422             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
423             ###LogSD $self->get_all_space . '::NamedWorksheet::load_unique_bits', );
424             ###LogSD $phone->talk( level => 'debug', message => [
425             ###LogSD "Setting the Worksheet unique bits", ] );
426              
427             # Read the sheet row-column dimensions
428 10         266 $self->_set_min_col( 1 );# As far as I can tell xml flat files don't acknowledge start point other than A1
429 10         255 $self->_set_min_row( 1 );
430 10         15 my $good_load = 0;
431 10         56 my $current_named_node = $self->current_named_node;
432             ###LogSD $phone->talk( level => 'debug', message => [
433             ###LogSD "Currently at named node:", $current_named_node, ] );
434 10         19 my( $result, $node_name, $node_level, $result_ref );
435 10 50       31 if( $current_named_node->{name} eq 'Table' ){
436             ###LogSD $phone->talk( level => 'debug', message => [
437             ###LogSD "already at the Table node" ] );
438 0         0 $result = 1;
439             }else{
440 10         47 ( $result, $node_name, $node_level, $result_ref ) =
441             $self->advance_element_position( 'Table' );
442             ###LogSD $phone->talk( level => 'info', message => [
443             ###LogSD "Advance to 'Table' node arrived at node -" .
444             ###LogSD "$node_name- with result: " . ($result//'fail'), ] );
445             }
446 10 50       52 if( $result ){
447 10         51 my $Table = $self->current_node_parsed;
448             ###LogSD $phone->talk( level => 'debug', message => [
449             ###LogSD "parsed Table value:", $Table ] );
450 10         20 my $end_column = $Table->{Table}->{'ss:ExpandedColumnCount'};
451 10         22 my $end_row = $Table->{Table}->{'ss:ExpandedRowCount'};
452             ###LogSD $phone->talk( level => 'debug', message => [
453             ###LogSD "End Column: $end_column", "End Row: $end_row" ] );
454 10         19 my ( $start_column, $start_row ) = ( 1, 1 );
455 10 100       263 $self->_set_max_col( $end_column ) if defined $end_column;
456 10 100       244 $self->_set_max_row( $end_row ) if defined $end_row;
457 10         30 $good_load = 1;
458             }else{
459 0         0 $self->_set_min_col( 0 );
460 0         0 $self->_set_min_row( 0 );
461 0         0 $self->set_error( "No sheet dimensions provided" );
462             }
463              
464             #pull column stats
465 10         32 ( $result, $node_name, $node_level, $result_ref ) =
466             $self->advance_element_position( 'Column' );
467             ###LogSD $phone->talk( level => 'debug', message => [
468             ###LogSD "attempt to get to the Column node arrived at -$node_name- with result: $result" ] );
469 10         25 my $column_store = [];
470 10         17 my $current_column = 1;# flat xml files don't always record column sometime they just sequence from the beginning
471 10         41 while( $node_name eq 'Column' ){
472 36         97 my $column_settings = $self->squash_node( $self->parse_element );
473             ###LogSD $phone->talk( level => 'debug', message => [
474             ###LogSD "Processing:", $column_settings ] );
475 36         42 my $col_ref;
476             map{
477 36 100       43 if( defined $column_settings->{$format_translations->{$_}} ){
  144         339  
478 52         158 $col_ref->{$_} = $column_settings->{$format_translations->{$_}}
479             }
480             } qw( width customWidth bestFit hidden );
481 36         52 $col_ref->{bestFit} = !$col_ref->{bestFit};
482 36 50       81 delete $col_ref->{bestFit} if !$col_ref->{bestFit};
483 36 100       143 $col_ref->{width} = $col_ref->{width} * $width_translation if exists $col_ref->{width};
484             ###LogSD $phone->talk( level => 'debug', message => [
485             ###LogSD "Updated column ref:", $col_ref ] );
486 36   66     95 my $start_column = $column_settings->{'ss:Index'} // $current_column;
487 36 100       88 my $end_column = $start_column + (exists( $column_settings->{'ss:Span'} ) ? $column_settings->{'ss:Span'} : 0);
488             ###LogSD $phone->talk( level => 'debug', message => [
489             ###LogSD "The column ref applies to columns -$start_column- through -$end_column-" ] );
490 36         59 for my $col ( $start_column .. $end_column ){
491 41         70 $column_store->[$col] = $col_ref;
492             ###LogSD $phone->talk( level => 'debug', message => [
493             ###LogSD "Updated column store is:", $column_store ] );
494             }
495 36         250 my $result = $self->next_sibling;
496 36 50       76 last if !$result;
497 36         75 $node_name = $self->current_named_node->{name};
498 36         173 $current_column = $end_column + 1;
499             ###LogSD $phone->talk( level => 'debug', message => [
500             ###LogSD "Currently at named node: $node_name", ".. it could be column: $current_column" ] );
501             }
502             ###LogSD $phone->talk( level => 'trace', message => [
503             ###LogSD "Final column store is:", $column_store ] );
504 10         291 $self->_set_column_formats( $column_store );
505              
506             #~ # Get cell merge and row hidden information
507             #~ if( $current_named_node->{name} eq 'Row' ){
508             #~ ###LogSD $phone->talk( level => 'debug', message => [
509             #~ ###LogSD "already at the Table node" ] );
510             #~ }else{
511             #~ $result = $self->advance_element_position( 'Row' );
512             #~ ###LogSD $phone->talk( level => 'debug', message => [
513             #~ ###LogSD "attempt to get to the Table node result: $result" ] );
514             #~ $current_named_node = $self->current_named_node;
515             #~ ###LogSD $phone->talk( level => 'debug', message => [
516             #~ ###LogSD "Currently at named node:", $current_named_node, ] );
517             #~ }
518             #~ my $merge_ref = [];
519             #~ my $row_store = [];
520             #~ my $current_row = 1;# flat xml files don't always record row sometime they just sequence from the beginning
521             #~ while( $current_named_node->{name} eq 'Row' ){
522             #~ my $row_settings = $self->current_node_parsed;
523             #~ ###LogSD $phone->talk( level => 'debug', message =>[ "Processing:", $row_settings ] );
524             #~ my $row_ref;
525             #~ map{
526             #~ if( defined $row_settings->{Row}->{$format_translations->{$_}} ){
527             #~ $row_ref->{$_} = $row_settings->{Row}->{$format_translations->{$_}}
528             #~ }
529             #~ } qw( width customWidth hidden );
530             #~ $row_ref->{bestFit} = !$row_ref->{bestFit};
531             #~ delete $row_ref->{bestFit} if !$row_ref->{bestFit};
532             #~ $row_ref->{width} = $row_ref->{width} * $width_translation if exists $row_ref->{width};
533             #~ ###LogSD $phone->talk( level => 'debug', message => [
534             #~ ###LogSD "Updated row ref:", $row_ref ] );
535             #~ $current_row = $row_settings->{Row}->{'ss:Index'} // $current_row;
536             #~ ###LogSD $phone->talk( level => 'debug', message => [
537             #~ ###LogSD "The row ref applies to row: $current_row" ] );
538             #~ $row_store->[$current_row] = $row_ref;
539             #~ $result = $self->advance_element_position( 'Cell' );
540             #~ ###LogSD $phone->talk( level => 'debug', message => [
541             #~ ###LogSD "attempt to get to the first Cell node result: $result" ] );
542             #~ $current_named_node = $self->current_named_node;
543             #~ ###LogSD $phone->talk( level => 'debug', message => [
544             #~ ###LogSD "Currently at named node:", $current_named_node, ] );
545             #~ my $current_column = 1;
546             #~ while( $current_named_node->{name} eq 'Cell' ){
547             #~ my $cell_settings = $self->current_node_parsed;
548             #~ ###LogSD $phone->talk( level => 'debug', message =>[ "Processing:", $cell_settings ] );
549             #~ my $cell_ref;
550             #~ map{
551             #~ if( defined $cell_settings->{Cell}->{$format_translations->{$_}} ){
552             #~ $cell_ref->{$_} = $cell_settings->{Cell}->{$format_translations->{$_}}
553             #~ }
554             #~ } qw( hidden mergeAcross mergeDown );
555             #~ ###LogSD $phone->talk( level => 'debug', message => [
556             #~ ###LogSD "Updated cell ref:", $cell_ref ] );
557             #~ $current_column = $cell_settings->{Cell}->{'ss:Index'} // $current_column;
558             #~ ###LogSD $phone->talk( level => 'debug', message => [
559             #~ ###LogSD "The cell ref applies to column: $current_column" ] );
560             #~ if( exists $cell_ref->{mergeAcross} or exists $cell_ref->{mergeDown} ){
561             #~ my $top_left = $self->_build_cell_label( $current_column, $current_row );
562             #~ ###LogSD $phone->talk( level => 'debug', message => [
563             #~ ###LogSD "Top left cell ID: $top_left" ] );
564             #~ my $right_column = $current_column + ($cell_ref->{mergeAcross}//0);
565             #~ my $bottom_row = $current_row + ($cell_ref->{mergeDown}//0);
566             #~ my $bottom_right = $self->_build_cell_label( $right_column, $bottom_row );
567             #~ my $merge_range = "$top_left:$bottom_right";
568             #~ ###LogSD $phone->talk( level => 'debug', message => [
569             #~ ###LogSD "Merge range is: $merge_range" ] );
570             #~ for my $row ( $current_row .. $bottom_row ){
571             #~ for my $col ( $current_column .. $right_column ){
572             #~ $merge_ref->[$row]->[$col] = $merge_range;
573             #~ }
574             #~ }
575             #~ ###LogSD $phone->talk( level => 'debug', message => [
576             #~ ###LogSD "Updated merge range:", $merge_ref ] );
577             #~ }
578             #~ my $result = $self->next_sibling;
579             #~ $current_named_node = $self->current_named_node;
580             #~ $current_column++;
581             #~ ###LogSD $phone->talk( level => 'debug', message => [
582             #~ ###LogSD "Currently at named node:", $current_named_node, ".. it could be column: $current_column" ] );
583             #~ }
584             #~ $current_row++;
585             #~ ###LogSD $phone->talk( level => 'debug', message => [
586             #~ ###LogSD ".. or it could be row: $current_row" ] );
587             #~ }
588             #~ ###LogSD $phone->talk( level => 'trace', message => [
589             #~ ###LogSD "Final row store is:", $row_store,
590             #~ ###LogSD "Final merge store is:", $merge_ref ] );
591             #~ $self->_set_row_formats( $row_store );
592             #~ $self->_set_merge_map( $merge_ref );
593              
594             #~ # Pull sheet hidden state
595             #~ $result = 1;
596             #~ if( $current_named_node->{name} eq 'Visible' ){
597             #~ ###LogSD $phone->talk( level => 'debug', message => [
598             #~ ###LogSD "already at the Visible node" ] );
599             #~ }else{
600             #~ $result = $self->advance_element_position( 'Visible' );
601             #~ ###LogSD $phone->talk( level => 'debug', message => [
602             #~ ###LogSD "attempt to get to the Visible node result: " . ($result//'failed') ] );
603             #~ $current_named_node = $self->current_named_node;
604             #~ ###LogSD $phone->talk( level => 'debug', message => [
605             #~ ###LogSD "Currently at named node:", $current_named_node, ] );
606             #~ }
607             #~ if( $result ){
608             #~ my $visible_node = $self->current_node_parsed;
609             #~ ###LogSD $phone->talk( level => 'trace', message => [
610             #~ ###LogSD "handling visible node:", $visible_node, ] );
611             #~ $self->_set_sheet_hidden( 1 ) if
612             #~ exists $visible_node->{Visible} and
613             #~ $visible_node->{Visible} eq 'SheetHidden';
614             #~ }
615              
616             # Record file state
617 10 50       29 if( $good_load ){
618             ###LogSD $phone->talk( level => 'debug', message => [
619             ###LogSD "The Worksheet file has metadata" ] );
620 10         287 $self->good_load( 1 );
621             }else{
622 0         0 $self->set_error( "No 'Worksheet' definition elements found - can't parse this as a Worksheet file" );
623 0         0 return undef;
624             }
625              
626             # Set the date parser
627 10 100       78 my $system_type = $self->get_epoch_year eq 1904 ? 'apple_excel' : 'win_excel' ;
628             ###LogSD $phone->talk( level => 'debug', message => [
629             ###LogSD "Setting Excel date type to: $system_type" ] );
630 10         219 $self->_set_date_parser( DateTimeX::Format::Excel->new( system_type => $system_type ) );
631              
632 10         47 $self->start_the_file_over;
633             ###LogSD $phone->talk( level => 'trace', message => [
634             ###LogSD "Finished the worksheet unique bits" ] );
635 10         67 return 1;
636             }
637              
638             after 'start_the_file_over' => sub{
639             my( $self, ) = @_;
640             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
641             ###LogSD $self->get_all_space . '::NamedWorksheet::start_the_file_over', );
642             ###LogSD $phone->talk( level => 'debug', message => [
643             ###LogSD "resetting the current row", ] );
644             $self->_set_current_row_number( 0 );
645             };
646              
647             #########1 Private Attributes 3#########4#########5#########6#########7#########8#########9
648              
649             has _sheet_min_col =>(
650             isa => Int,
651             writer => '_set_min_col',
652             reader => '_min_col',
653             predicate => 'has_min_col',
654             );
655              
656             has _sheet_min_row =>(
657             isa => Int,
658             writer => '_set_min_row',
659             reader => '_min_row',
660             predicate => 'has_min_row',
661             );
662              
663             has _sheet_max_col =>(
664             isa => Int,
665             writer => '_set_max_col',
666             reader => '_max_col',
667             predicate => 'has_max_col',
668             );
669              
670             has _sheet_max_row =>(
671             isa => Int,
672             writer => '_set_max_row',
673             reader => '_max_row',
674             predicate => 'has_max_row',
675             );
676              
677             has _sheet_min_col =>(
678             isa => Int,
679             writer => '_set_min_col',
680             reader => '_min_col',
681             predicate => 'has_min_col',
682             );
683              
684             has _current_row_number =>(# Counting from 1
685             isa => Int,
686             writer => '_set_current_row_number',
687             reader => '_get_current_row_number',
688             );
689              
690             has _column_formats =>(
691             isa => ArrayRef,
692             traits => ['Array'],
693             writer => '_set_column_formats',
694             reader => '_get_column_formats',
695             default => sub{ [] },
696             handles =>{
697             get_custom_column_data => 'get',
698             },
699             );
700              
701             has _row_formats =>(
702             isa => ArrayRef,
703             traits => ['Array'],
704             writer => '_set_row_formats',
705             reader => '_get_row_formats',
706             default => sub{ [] },
707             handles =>{
708             get_custom_row_data => 'get',
709             _set_custom_row_data => 'set',
710             },
711             );
712              
713             has _merge_map =>(
714             isa => ArrayRef,
715             traits => ['Array'],
716             writer => '_set_merge_map',
717             reader => 'get_merge_map',
718             default => sub{ [] },
719             handles =>{
720             _get_row_merge_map => 'get',
721             _set_row_merge_map => 'set',
722             },
723             );
724              
725             has _primary_merged_values =>(# Values for the top left corner of the merge range
726             isa => HashRef,
727             traits => ['Hash'],
728             reader => '_get_all_merged_values',
729             default => sub{ {} },
730             handles =>{
731             _set_merged_value => 'set',
732             _get_merged_value => 'get',
733             },
734             );
735              
736             has _date_parser =>(
737             isa => 'DateTimeX::Format::Excel',
738             writer => '_set_date_parser',
739             handles => { _format_datetime => 'format_datetime' },
740             );
741              
742             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
743              
744             sub _process_data_element{
745 22     22   31 my( $self, $element_ref, $cell_raw_text, $rich_text, $wrapper_ref ) = @_;
746             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
747             ###LogSD $self->get_all_space . '::NamedWorksheet::_process_data_element', );
748             ###LogSD $phone->talk( level => 'info', message => [
749             ###LogSD "Adding to raw_text: " . ($cell_raw_text//'undef'), '..and rich_text:', $rich_text,
750             ###LogSD "..using wrapper ref:", $wrapper_ref, '..with element:', $element_ref ] );
751              
752             # Handle cell type
753 22 100       43 my $element_type = exists $element_ref->{'ss:Type'} ? $element_ref->{'ss:Type'} : 'Text';
754 22         22 delete $element_ref->{'ss:Type'};
755             ###LogSD $phone->talk( level => 'debug', message =>[ "Cell type is: $element_type" ] );
756              
757             # Handle xmlns element
758 22         19 delete $element_ref->{xmlns};
759             ###LogSD $phone->talk( level => 'debug', message =>[ "after xmlns actions - updated element: ", $element_ref ] );
760              
761             # Handle Font element
762 22 100       40 if( exists $element_ref->{Font} ){
763 12         9 my $rich_ref;
764 12         16 for my $setting ( qw( html:Color html:Size ) ){
765 24 100       71 $rich_ref->{$rich_text_translations->{$setting}} = $element_ref->{Font}->{$setting} if exists $element_ref->{Font}->{$setting};
766             ###LogSD $phone->talk( level => 'debug', message =>[
767             ###LogSD "After adding -$rich_text_translations->{$setting}- for -$setting- result:", $rich_ref, ] );
768             }
769 12 50       20 if( exists $rich_ref->{rgb} ){
770 12 100       22 if( $rich_ref->{rgb} ne '#000000' ){
771 8         14 $rich_ref->{color}->{rgb} = $rich_ref->{rgb};
772 8         19 $rich_ref->{color}->{rgb} =~ s/#/FF/;
773             }
774 12         15 delete $rich_ref->{rgb};
775             }
776 12 100       27 if( keys %$rich_ref ){
777 8 50       14 if( @$wrapper_ref ){
778 8         8 map{ $rich_ref->{$_} = undef } @$wrapper_ref;
  8         16  
779             }
780 8         9 push @$rich_text, length( $cell_raw_text ), $rich_ref,
781             }
782 12         16 $cell_raw_text .= $element_ref->{Font}->{raw_text};
783             ###LogSD $phone->talk( level => 'debug', message =>[
784             ###LogSD "Updated raw_text -$cell_raw_text- with rich_text:", $rich_text, ] );
785 12         19 delete $element_ref->{Font};
786             }
787             ###LogSD $phone->talk( level => 'debug', message =>[ "after font actions - updated element: ", $element_ref ] );
788              
789             # Handle list element here
790 22         20 for my $sub_element ( @{$element_ref->{list}} ){
  22         38  
791 8         15 $sub_element = { 'Font' => $sub_element };
792             ###LogSD $phone->talk( level => 'debug', message =>[
793             ###LogSD "Handling list element:", $sub_element ] );
794 8         19 ( $cell_raw_text, $rich_text, ) = $self->_process_data_element( $sub_element, $cell_raw_text, $rich_text, $wrapper_ref );
795             ###LogSD $phone->talk( level => 'debug', message =>[
796             ###LogSD "Returned raw_text: $cell_raw_text", $rich_text ] );
797             }
798 22         37 delete $element_ref->{list};
799             ###LogSD $phone->talk( level => 'debug', message =>[ "after list actions - updated element: ", $element_ref ] );
800              
801             # Handle remaining node(s)
802 22 100       53 if( scalar (keys %$element_ref) == 0 ){
    50          
803             ###LogSD $phone->talk( level => 'debug', message =>[
804             ###LogSD "All done with -$element_type- node raw_text |" . ($cell_raw_text//'') . "|", $rich_text ] );
805             }elsif( scalar (keys %$element_ref) == 1 ){
806             ###LogSD $phone->talk( level => 'debug', message =>[
807             ###LogSD "Found a wrapper node: " . (keys %$element_ref)[0] ] );
808 4         16 push @$wrapper_ref, lc((keys %$element_ref)[0]);
809 4         30 ( $cell_raw_text, $rich_text, ) = $self->_process_data_element( $element_ref->{(keys %$element_ref)[0]}, $cell_raw_text, $rich_text, $wrapper_ref );
810 4         6 pop @$wrapper_ref;
811             ###LogSD $phone->talk( level => 'debug', message =>[
812             ###LogSD "Returned raw_text: $cell_raw_text", $rich_text, $wrapper_ref ] );
813             }else{
814 0         0 confess "I found more nodes than expected at this point: " . join( ', ', keys %$element_ref );
815             }
816             ###LogSD $phone->talk( level => 'debug', message =>[ "Updated element: ", $element_ref ] );
817              
818             ###LogSD $phone->talk( level => 'info', message => [
819             ###LogSD "Returning raw_text: " . ($cell_raw_text//'undef'), "..of data type: $element_type", '..with rich_text:', $rich_text ] );
820 22         60 return( $cell_raw_text, $rich_text, $element_type );
821             }
822              
823             #########1 Phinish 3#########4#########5#########6#########7#########8#########9
824              
825 5     5   12362 no Moose::Role;
  5         16  
  5         40  
826              
827             1;
828              
829             #########1 Documentation 3#########4#########5#########6#########7#########8#########9
830             __END__
831              
832             =head1 NAME
833              
834             Spreadsheet::Reader::ExcelXML::XMLReader::NamedWorksheet - Flat XML Excel worksheet interpreter
835              
836             =head1 SYNOPSIS
837              
838             See t\Spreadsheet\Reader\ExcelXML\XMLReader\06-named_worksheet.t
839              
840             =head1 DESCRIPTION
841              
842             This documentation is written to explain ways to use this module when writing your own excel
843             parser. To use the general package for excel parsing out of the box please review the
844             documentation for L<Workbooks|Spreadsheet::Reader::ExcelXML>,
845             L<Worksheets|Spreadsheet::Reader::ExcelXML::Worksheet>, and
846             L<Cells|Spreadsheet::Reader::ExcelXML::Cell>
847              
848             This module incrementally adds functionality to the base class
849             L<Spreadsheet::Reader::ExcelXML::XMLReader>. The goal is to parse individual worksheet files
850             (not chartsheets) from the flat XML Excel file format (.xml) into perl objects The primary
851             purpose of this role is to normalize functions used by L<Spreadsheet::Reader::ExcelXML::WorksheetToRow>
852             where other roles could be used to normalize other formats. It does not provide a way to read
853             L<chartsheets|Spreadsheet::Reader::ExcelXML::Chartsheet>.
854              
855             I<All positions (row and column places and integers) at this level are stored and returned
856             in count from one mode!>
857              
858             To replace this part in the package look in the raw code of
859             L<Spreadsheet::Reader::ExcelXML::Workbook> and adjust the 'worksheet_interface' key of the
860             $parser_modules variable.
861              
862             =head2 requires
863              
864             This module is a L<role|Moose::Manual::Roles> and as such only adds incremental methods and
865             attributes to some base class. In order to use this role some base object methods are
866             required. The requirments are listed below with links to the default provider.
867              
868             =over
869              
870             L<Spreadsheet::Reader::ExcelXML::XMLReader/current_named_node>
871              
872             L<Spreadsheet::Reader::ExcelXML::XMLReader/current_node_parsed>
873              
874             L<Spreadsheet::Reader::ExcelXML::XMLReader/advance_element_position>
875              
876             L<Spreadsheet::Reader::ExcelXML::XMLReader/good_load>
877              
878             L<Spreadsheet::Reader::ExcelXML::XMLReader/start_the_file_over>
879              
880             L<Spreadsheet::Reader::ExcelXML::XMLReader/squash_node>
881              
882             L<Spreadsheet::Reader::ExcelXML::XMLReader/parse_element>
883              
884             "_build_cell_label" in L<Spreadsheet::Reader::ExcelXML::CellToColumnRow
885             |Spreadsheet::Reader::ExcelXML::CellToColumnRow/build_cell_label( $column, $row, )>
886              
887             L<Spreadsheet::Reader::ExcelXML::Workbook/get_epoch_year>
888              
889             L<Spreadsheet::Reader::ExcelXML::Workbook/spreading_merged_values>
890              
891             L<Spreadsheet::Reader::ExcelXML::Workbook/should_skip_hidden>
892              
893             L<Spreadsheet::Reader::ExcelXML::Workbook/are_spaces_empty>
894              
895             L<Spreadsheet::Reader::ExcelXML::Workbook/get_empty_return_type>
896              
897             L<Spreadsheet::Reader::ExcelXML::Workbook/get_values_only>
898              
899             =back
900              
901             =head2 Attributes
902              
903             Data passed to new when creating an instance. This list only contains public attributes
904             incrementally provided by this role. For access to the values in these attributes see
905             the listed 'attribute methods'. For general information on attributes see
906             L<Moose::Manual::Attributes>. For ways to manage the instance when opened see the
907             L<Methods|/Methods>.
908              
909             =head3 is_hidden
910              
911             =over
912              
913             B<Definition:> This data is collected at the worksheet level for this file type. It indicates
914             if the sheet is human visible. Since the data is collected during the implementation of
915             load_unique_bits it will always overwrite what is passed from the Workbook.
916              
917             B<Range:> (1|0)
918              
919             B<attribute methods> Methods provided to adjust this attribute
920              
921             =over
922              
923             B<is_sheet_hidden>
924              
925             =over
926              
927             B<Definition:> return the attribute value
928              
929             =back
930              
931             =back
932              
933             =back
934              
935             =head3 _sheet_min_col
936              
937             =over
938              
939             B<Definition:> This is the minimum column in the sheet with data or formatting. For this
940             module it is pulled from the xml file at worksheet/dimension:ref = "upperleft:lowerright"
941              
942             B<Range:> an integer
943              
944             B<attribute methods> Methods provided to adjust this attribute
945              
946             =over
947              
948             B<_min_col>
949              
950             =over
951              
952             B<Definition:> returns the attribute value
953              
954             =back
955              
956             B<has_min_col>
957              
958             =over
959              
960             B<Definition:> attribute predicate
961              
962             =back
963              
964             =back
965              
966             =back
967              
968             =head3 _sheet_min_row
969              
970             =over
971              
972             B<Definition:> This is the minimum row in the sheet with data or formatting. For this
973             module it is pulled from the xml file at worksheet/dimension:ref = "upperleft:lowerright"
974              
975             B<Range:> an integer
976              
977             B<attribute methods> Methods provided to adjust this attribute
978              
979             =over
980              
981             B<_set_min_row>
982              
983             =over
984              
985             B<Definition:> sets the attribute value
986              
987             =back
988              
989             B<_min_row>
990              
991             =over
992              
993             B<Definition:> returns the attribute value
994              
995             =back
996              
997             B<has_min_row>
998              
999             =over
1000              
1001             B<Definition:> attribute predicate
1002              
1003             =back
1004              
1005             =back
1006              
1007             =back
1008              
1009             =head3 _sheet_max_col
1010              
1011             =over
1012              
1013             B<Definition:> This is the maximum column in the sheet with data or formatting. For this
1014             module it is pulled from the xml file at worksheet/dimension/ref = "upperleft:lowerright"
1015              
1016             B<Range:> an integer
1017              
1018             B<attribute methods> Methods provided to adjust this attribute
1019              
1020             =over
1021              
1022             B<_set_max_col>
1023              
1024             =over
1025              
1026             B<Definition:> sets the attribute value
1027              
1028             =back
1029              
1030             B<_max_col>
1031              
1032             =over
1033              
1034             B<Definition:> returns the attribute value
1035              
1036             =back
1037              
1038             B<has_max_col>
1039              
1040             =over
1041              
1042             B<Definition:> attribute predicate
1043              
1044             =back
1045              
1046             =back
1047              
1048             =back
1049              
1050             =head3 _sheet_max_row
1051              
1052             =over
1053              
1054             B<Definition:> This is the maximum row in the sheet with data or formatting. For this
1055             module it is pulled from the xml file at worksheet/dimension:ref = "upperleft:lowerright"
1056              
1057             B<Range:> an integer
1058              
1059             B<attribute methods> Methods provided to adjust this attribute
1060              
1061             =over
1062              
1063             B<_set_max_row>
1064              
1065             =over
1066              
1067             B<Definition:> sets the attribute value
1068              
1069             =back
1070              
1071             B<_max_row>
1072              
1073             =over
1074              
1075             B<Definition:> returns the attribute value
1076              
1077             =back
1078              
1079             B<has_max_row>
1080              
1081             =over
1082              
1083             B<Definition:> attribute predicate
1084              
1085             =back
1086              
1087             =back
1088              
1089             =back
1090              
1091             =head2 Methods
1092              
1093             These are the methods provided by this class for use within the package but are not intended
1094             to be used by the end user. Other private methods not listed here are used in the module but
1095             not used by the package. If a method is listed here then replacement of this module
1096             either requires replacing the method or rewriting all the associated connecting roles and classes.
1097              
1098             =head3 load_unique_bits
1099              
1100             =over
1101              
1102             B<Definition:> This is called by L<Spreadsheet::Reader::ExcelXML::XMLReader> when the file is
1103             loaded for the first time so that file specific metadata can be collected.
1104              
1105             B<Accepts:> nothing
1106              
1107             B<Returns:> nothing
1108              
1109             =back
1110              
1111             =head3 advance_row_position( $increment )
1112              
1113             =over
1114              
1115             B<Definition:> As an XML data structure each worksheet has three levels of information. The
1116             column data is stored separately in the file and just referenced. The row data encases all
1117             the cell data for that row. Each cell contains modifiers to row and column settings. The
1118             column data is read during the 'load_unique_bits' method. The cell specific data is not
1119             completed here. This method will advance to the next recorded row position in the XML file.
1120             Not to be confused with the next row number. If you want to advance to the 'next' position
1121             more than one time then you can provide a value for $increment.
1122              
1123             B<Accepts:> a positive integer $increment (defaults to 1 if no value passed)
1124              
1125             B<Returns:> The attribute ref of the top row node
1126              
1127             =back
1128              
1129             =head3 build_row_data
1130              
1131             =over
1132              
1133             B<Definition:> Collects all the sub-information (XML node) for the row in order to build
1134             the argument for populating a L<Spreadsheet::Reader::ExcelXML::Row> instance.
1135              
1136             B<Accepts:> nothing
1137              
1138             B<Returns:> a hash ref of inputs for L<Spreadsheet::Reader::ExcelXML::Row>
1139              
1140             =back
1141              
1142             =head3 get_custom_column_data( $column )
1143              
1144             =over
1145              
1146             B<Definition:> Returns any collected custom column information for the indicated
1147             $column.
1148              
1149             B<Accepts:> a positive integer $column in count from 1 context
1150              
1151             B<Returns:> a hash ref of custom column settings
1152              
1153             =back
1154              
1155             =head3 get_custom_row_data( $row )
1156              
1157             =over
1158              
1159             B<Definition:> Returns any collected custom row information for the indicated $row.
1160              
1161             B<Accepts:> a positive integer $row in count from 1 context
1162              
1163             B<Returns:> a hash ref of custom row settings
1164              
1165             =back
1166              
1167             =head3 get_merge_map
1168              
1169             =over
1170              
1171             B<Definition:> This returns the full merge map with merge ranges stored in each
1172             position for the range of known rows and columns.
1173              
1174             B<Accepts:> nothing
1175              
1176             B<Returns:> an array ref of array refs where the top level array represents
1177             rows stored in count from 1 context and the second level array ref are the
1178             columns stored in count from 1 context. (The first position for each will
1179             therefor be dead space)
1180              
1181             =back
1182              
1183             =head1 SUPPORT
1184              
1185             =over
1186              
1187             L<github Spreadsheet::Reader::ExcelXML/issues
1188             |https://github.com/jandrew/p5-spreadsheet-reader-excelxml/issues>
1189              
1190             =back
1191              
1192             =head1 TODO
1193              
1194             =over
1195              
1196             B<1.> If a the primary cell of a merge range is hidden show that value
1197             in the top left unhidden cell even when the attribute
1198             L<Spreadsheet::Reader::ExcelXML::Workbook/spread_merged_values> is not
1199             set. (This is the way excel does it(ish))
1200              
1201             =back
1202              
1203             =head1 AUTHOR
1204              
1205             =over
1206              
1207             =item Jed Lund
1208              
1209             =item jandrew@cpan.org
1210              
1211             =back
1212              
1213             =head1 COPYRIGHT
1214              
1215             This program is free software; you can redistribute
1216             it and/or modify it under the same terms as Perl itself.
1217              
1218             The full text of the license can be found in the
1219             LICENSE file included with this module.
1220              
1221             This software is copyrighted (c) 2016 by Jed Lund
1222              
1223             =head1 DEPENDENCIES
1224              
1225             =over
1226              
1227             L<Spreadsheet::Reader::ExcelXML> - the package
1228              
1229             =back
1230              
1231             =head1 SEE ALSO
1232              
1233             =over
1234              
1235             L<Spreadsheet::Read> - generic Spreadsheet reader
1236              
1237             L<Spreadsheet::ParseExcel> - Excel binary version 2003 and earlier (.xls files)
1238              
1239             L<Spreadsheet::XLSX> - Excel version 2007 and later
1240              
1241             L<Spreadsheet::ParseXLSX> - Excel version 2007 and later
1242              
1243             L<Log::Shiras|https://github.com/jandrew/Log-Shiras>
1244              
1245             =over
1246              
1247             All lines in this package that use Log::Shiras are commented out
1248              
1249             =back
1250              
1251             =back
1252              
1253             =cut
1254              
1255             #########1 Documentation End 3#########4#########5#########6#########7#########8#########9