File Coverage

lib/Spreadsheet/Reader/ExcelXML/XMLReader/PositionStyles.pm
Criterion Covered Total %
statement 141 170 82.9
branch 76 112 67.8
condition 7 18 38.8
subroutine 13 13 100.0
pod 3 3 100.0
total 240 316 75.9


line stmt bran cond sub pod time code
1             package Spreadsheet::Reader::ExcelXML::XMLReader::PositionStyles;
2             our $AUTHORITY = 'cpan:JANDREW';
3 20     20   66435 use version; our $VERSION = version->declare('v0.16.8');
  20         32  
  20         140  
4             ###LogSD warn "You uncovered internal logging statements for Spreadsheet::Reader::ExcelXML::XMLReader::PositionStyles-$VERSION";
5              
6 20     20   2352 use 5.010;
  20         58  
7 20     20   87 use Moose::Role;
  20         28  
  20         158  
8             requires qw(
9             get_defined_conversion start_the_file_over close_the_file
10             advance_element_position parse_element set_defined_excel_formats
11             current_named_node set_error squash_node
12             good_load
13             );
14 20         184 use Types::Standard qw(
15             Bool ArrayRef Int is_HashRef is_Int
16 20     20   76149 );
  20         32  
17 20     20   15583 use Carp qw( confess );
  20         34  
  20         1129  
18 20     20   86 use Clone qw( clone );
  20         22  
  20         30914  
19              
20             #########1 Dispatch Tables & Package Variables 5#########6#########7#########8#########9
21              
22             my $element_lookup ={
23             numFmts => 'numFmt',
24             fonts => 'font',
25             borders => 'border',
26             fills => 'fill',
27             cellStyleXfs => 'xf',
28             cellXfs => 'xf',
29             cellStyles => 'cellStyle',
30             tableStyles => 'tableStyle',
31             dxfs => 'dxf',
32             };
33              
34             my $key_translations ={
35             fontId => 'fonts',
36             borderId => 'borders',
37             fillId => 'fills',
38             xfId => 'cellStyles',
39             dxfId => 'dxfs',
40             #~ pivotButton => 'pivotButton',
41             };
42              
43             my $cell_attributes ={
44             fontId => 'cell_font',
45             borderId => 'cell_border',
46             fillId => 'cell_fill',
47             xfId => 'cell_style',
48             numFmtId => 'cell_coercion',
49             alignment => 'cell_alignment',
50             numFmts => 'cell_coercion',
51             fonts => 'cell_font',
52             borders => 'cell_border',
53             fills => 'cell_fill',
54             dxfId => 'table_style',
55             cellStyleXfs => 'cellStyleXfs',
56             cellXfs => 'cellXfs',
57             cellStyles => 'cell_style',
58             tableStyles => 'tableStyle',
59             pivotButton => 'pivotButton',
60             };
61              
62             my $xml_from_cell ={
63             cell_font => 'fontId',
64             cell_border => 'borderId',
65             cell_fill => 'fillId',
66             cell_style => 'xfId',
67             cell_coercion => 'numFmtId',
68             cell_alignment => 'alignment',
69             };
70              
71             #########1 Public Attributes 3#########4#########5#########6#########7#########8#########9
72              
73             has cache_positions =>(
74             isa => Bool,
75             reader => 'should_cache_positions',
76             default => 1,
77             );
78              
79             #########1 Public Methods 3#########4#########5#########6#########7#########8#########9
80              
81             sub get_format{
82 297     297 1 30433 my( $self, $position, $header, $exclude_header ) = @_;
83 297 100       515 my $xml_target_header = $header ? $header : '';#$xml_from_cell->{$header}
84 297 100       534 my $xml_exclude_header = $exclude_header ? $xml_from_cell->{$exclude_header} : '';
85             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
86             ###LogSD $self->get_all_space . '::get_format', );
87             ###LogSD $phone->talk( level => 'info', message => [
88             ###LogSD "Get defined formats at position: $position",
89             ###LogSD ( $header ? "Returning only the values for header: $header - $xml_target_header" : '' ),
90             ###LogSD ( $exclude_header ? "..excluding the values for header: $exclude_header - $xml_exclude_header" : '' ) , ] );
91              
92             # Check for stored value - when caching implemented
93 297         269 my $already_got_it = 0;
94 297 100       7661 if( $self->_has_styles_positions ){
95 288 50       6777 if( $position > $self->_get_styles_count - 1 ){
96 0         0 $self->set_error( "Requested styles position is out of range for this workbook" );
97 0         0 return undef;
98             }
99 288         7808 my $target_ref = clone( $self->_get_s_position( $position ) );
100             ###LogSD $phone->talk( level => 'debug', message => [
101             ###LogSD "The complete cached style is:", $target_ref, ] );
102 288 100       1921 if( $header ){
    100          
103 140 50       634 $target_ref = $target_ref->{$header} ? { $header => $target_ref->{$header} } : undef;
104             ###LogSD $phone->talk( level => 'debug', message => [
105             ###LogSD "The cached style with target header -$header- only is:", $target_ref ] );
106             }elsif( $exclude_header ){
107 12         54 delete $target_ref->{$exclude_header};
108             ###LogSD $phone->talk( level => 'debug', message => [
109             ###LogSD "The cached style with exclude header -$exclude_header- removed is:", $target_ref ] );
110             }
111 288         3800 return $target_ref;
112             }
113              
114             # pull the data the long (hard and slow) way
115             # Pull the base ref
116 9         34 my( $success, $base_ref ) = $self->_get_header_and_value( 'cellXfs', $position );
117 9 50       27 if( !$success ){
118 0         0 confess "Unable to pull position -$position- of the base stored formats (cellXfs)";
119             }
120             ###LogSD $phone->talk( level => 'debug', message => [
121             ###LogSD "Updated base ref:", $base_ref ] );
122              
123 9         55 my $built_ref = $self->_build_cell_style_formats( $base_ref, $xml_target_header, $xml_exclude_header );
124             ###LogSD $phone->talk( level => 'debug', message => [
125             ###LogSD "Updated built ref:", $built_ref ] );
126 9         96 return $built_ref;
127             }
128              
129             sub get_default_format{
130 8     8 1 7944 my( $self, $header, $exclude_header ) = @_;
131 8         16 my $position = 0;
132 8 100       27 my $xml_target_header = $header ? $xml_from_cell->{$header} : '';
133 8 50       21 my $xml_exclude_header = $exclude_header ? $xml_from_cell->{$exclude_header} : '';
134             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
135             ###LogSD $self->get_all_space . '::get_default_format', );
136             ###LogSD $phone->talk( level => 'info', message => [
137             ###LogSD "Get defined formats at default position: $position",
138             ###LogSD ( $header ? "Returning only the values for header: $header - $xml_target_header" : '' ),
139             ###LogSD ( $exclude_header ? "..excluding the values for header: $exclude_header - $xml_exclude_header" : '' ) , ] );
140              
141             # Check for stored value - when caching implemented
142 8         14 my $already_got_it = 0;
143 8 100       325 if( $self->_has_generic_styles_positions ){
144 5 50       140 if( $position > $self->_get_generic_styles_count - 1 ){
145 0         0 $self->set_error( "Requested default styles position is out of range for this workbook" );
146 0         0 return undef;
147             }
148 5         145 my $target_ref = $self->_get_gs_position( $position );
149             ###LogSD $phone->talk( level => 'debug', message => [
150             ###LogSD "The complete cached style is:", $target_ref ] );
151 5 100       16 if( $header ){
    50          
152 2 50       21 $target_ref = $target_ref->{$header} ? { $header => $target_ref->{$header} } : undef;
153             ###LogSD $phone->talk( level => 'debug', message => [
154             ###LogSD "The cached style with target header only is:", $target_ref ] );
155             }elsif( $exclude_header ){
156 0         0 delete $target_ref->{$exclude_header};
157             ###LogSD $phone->talk( level => 'debug', message => [
158             ###LogSD "The cached style with exclude header -$exclude_header- removed is:", $target_ref ] );
159             }
160 5         33 return $target_ref;
161             }
162              
163             # pull the data the long (hard and slow) way
164             # Pull the base ref
165 3         14 my( $key, $base_ref ) = $self->_get_header_and_value( 'cellStyleXfs', $position );
166 3 50       13 if( !$key ){
167 0         0 confess "Unable to pull the default position (0) for stored formats (cellStyleXfs)";
168             }
169             ###LogSD $phone->talk( level => 'debug', message => [
170             ###LogSD "Received key -$key- with value:", $base_ref ] );
171              
172 3         160 my $built_ref = $self->_build_cell_style_formats( $base_ref, $xml_target_header, $xml_exclude_header );
173             ###LogSD $phone->talk( level => 'debug', message => [
174             ###LogSD "Updated built ref:", $built_ref ] );
175 3         39 return $built_ref;
176             }
177              
178             sub load_unique_bits{
179 26     26 1 61 my( $self, ) = @_;
180             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
181             ###LogSD $self->get_all_space . '::load_unique_bits', );
182             #~ ###LogSD $phone->talk( level => 'trace', message => [ 'self:', $self ] );
183              
184             # Advance to the styleSheet node
185 26         45 my( $result, $node_name, $node_level, $result_ref );
186 26         169 my $current_node = $self->current_node_parsed;
187             ###LogSD $phone->talk( level => 'trace', message =>[
188             ###LogSD "The current node is:", $current_node ] );
189 26 50       130 if( (keys %$current_node)[0] eq 'styleSheet' ){
190             ###LogSD $phone->talk( level => 'trace', message =>[
191             ###LogSD "Found the core properties node" ] );
192 26         40 $result = 2;
193 26         379 $node_name = 'styleSheet';
194             }else{
195 0         0 ( $result, $node_name, $node_level, $result_ref ) =
196             $self->advance_element_position( 'styleSheet' );
197             }
198              
199             # Record file state
200 26 50       474 if( $result ){
201             ###LogSD $phone->talk( level => 'debug', message => [
202             ###LogSD "The styleSheet node has value" ] );
203 26         821 $self->good_load( 1 );# Is there a need to check for an empty node here???
204             }else{
205 0         0 $self->set_error( "No 'styleSheet' elements with content found - can't parse this as a styles file" );
206 0         0 return undef;
207             }
208              
209             # Initial pull from the xml
210 26         46 my ( $success, $custom_format_ref, $top_level_ref );
211 26 100       686 if( $self->should_cache_positions ){
212 23         208 $top_level_ref = $self->parse_element;
213             ###LogSD $phone->talk( level => 'trace', message => [
214             ###LogSD "Parsing the whole thing for caching" ] );
215 23         209 $self->close_the_file;# Don't need the file open any more!
216              
217 23         115 $top_level_ref = $self->squash_node( $top_level_ref );#, 'numFmts'
218             ###LogSD $phone->talk( level => 'trace', message => [
219             ###LogSD "Squashed ref:", $top_level_ref ] );
220             $custom_format_ref =
221             !exists $top_level_ref->{numFmts} ? undef :
222             exists $top_level_ref->{numFmts}->{list} ?
223             $top_level_ref->{numFmts}->{list} :
224 23 100       157 [ $top_level_ref->{numFmts}->{numFmt} ] ;
    100          
225             ###LogSD $phone->talk( level => 'trace', message => [
226             ###LogSD "Initial extraction of numFmts:", $custom_format_ref ] );
227             }else{
228 3         28 ( $result, $node_name, $node_level, $result_ref ) =
229             $self->advance_element_position( 'numFmts' );
230 3 50       10 if( $result ){
231 3         14 $top_level_ref = $self->parse_element;
232             ###LogSD $phone->talk( level => 'debug', message => [
233             ###LogSD "Pulling the custom number formats only:", $custom_format_ref ] );
234 3         10 $top_level_ref = $self->squash_node( $top_level_ref );#, 'numFmts'
235             ###LogSD $phone->talk( level => 'debug', message => [
236             ###LogSD "Squashed ref:", $top_level_ref ] );
237             $custom_format_ref = exists $top_level_ref->{list} ?
238 3 50       13 $top_level_ref->{list} : [ $top_level_ref->{numFmt} ] ;
239             ###LogSD $phone->talk( level => 'trace', message => [
240             ###LogSD "Initial extraction of numFmts:", $custom_format_ref ] );
241             }
242 3         22 $self->start_the_file_over;
243             }
244              
245             # Load the custom formats
246 26 100       97 if( $custom_format_ref ){
247 17         28 my $translations;
248 17         38 for my $format ( @$custom_format_ref ){
249             ###LogSD $phone->talk( level => 'debug', message => [
250             ###LogSD "Adding sheet defined translations:", $format ] );
251 21         41 my $format_code = $format->{formatCode};
252 21         86 $format_code =~ s/\\//g;
253 21         155 $translations->[$format->{numFmtId}] = $format_code;
254             }
255             ###LogSD $phone->talk( level => 'trace', message => [
256             ###LogSD 'loading format positions:', $translations ] );
257 17         161 $self->set_defined_excel_formats( $translations );
258             }
259              
260             # Cache remaining as needed
261 26         5760 my( $list_to_cache, $count );
262 26 100       846 if( $self->should_cache_positions ){
263             ###LogSD $phone->talk( level => 'trace', message => [
264             ###LogSD "Build and load the rest of the cache", $top_level_ref ] );
265              
266             # Build specfic formats
267 23 50 33     251 if( !exists $top_level_ref->{cellXfs} or
268             (!exists $top_level_ref->{cellXfs}->{list} and !exists $top_level_ref->{cellXfs}->{xf}) ){
269 0         0 $self->set_error( "No base level formats (cellXfs) stored" );
270             }else{
271             my $cell_xfs = exists $top_level_ref->{cellXfs}->{list} ?
272 23 100       95 $top_level_ref->{cellXfs}->{list} : [ $top_level_ref->{cellXfs}->{xf} ];
273             ###LogSD $phone->talk( level => 'trace', message => [
274             ###LogSD "Loading specific cell formats:", $cell_xfs ] );
275 23         691 $self->_set_styles_count( scalar( @$cell_xfs ) );
276 23         56 for my $position ( @$cell_xfs ){
277 331         543 my $stacked_ref = $self->_stack_perl_ref( $top_level_ref, $position );
278             ###LogSD $phone->talk( level => 'trace', message => [
279             ###LogSD "Updated position:", $stacked_ref ] );
280 331         9036 $self->_add_s_position( $stacked_ref );
281             }
282             ###LogSD $phone->talk( level => 'trace', message => [
283             ###LogSD "Final specific caches:", $self->_get_all_cache ] );
284             }
285              
286             # Build generic formats
287 23 50 66     221 if( exists $top_level_ref->{cellStyleXfs} and
      33        
288             (exists $top_level_ref->{cellStyleXfs}->{list} or exists $top_level_ref->{cellStyleXfs}->{xf}) ){
289             my $cell_style_xfs = exists $top_level_ref->{cellStyleXfs}->{list} ?
290 23 100       92 $top_level_ref->{cellStyleXfs}->{list} : [ $top_level_ref->{cellStyleXfs}->{xf} ];
291             ###LogSD $phone->talk( level => 'debug', message => [
292             ###LogSD "Loading generic cell formats" ] );
293 23         688 $self->_set_generic_styles_count( scalar( @$cell_style_xfs ) );
294 23         56 for my $position ( @$cell_style_xfs ){
295 24         93 my $stacked_ref = $self->_stack_perl_ref( $top_level_ref, $position );
296             ###LogSD $phone->talk( level => 'trace', message => [
297             ###LogSD "Updated position:", $stacked_ref ] );
298 24         731 $self->_add_gs_position( $stacked_ref );
299             }
300             ###LogSD $phone->talk( level => 'trace', message => [
301             ###LogSD "Final generic caches:", $self->_get_all_generic_cache ] );
302             }
303             ###LogSD $phone->talk( level => 'trace', message => [
304             ###LogSD "Completed caching" ] );
305             }
306 26         853 return 1;
307             }
308              
309             #########1 Private Attributes 3#########4#########5#########6#########7#########8#########9
310              
311             has _styles_positions =>(
312             isa => ArrayRef,
313             traits => ['Array'],
314             handles =>{
315             _get_s_position => 'get',
316             _set_s_position => 'set',
317             _add_s_position => 'push',
318             },
319             reader => '_get_all_cache',
320             predicate => '_has_styles_positions'
321             );
322              
323             has _styles_count =>(
324             isa => Int,
325             default => 0,
326             reader => '_get_styles_count',
327             writer => '_set_styles_count',
328             );
329              
330             has _generic_styles_positions =>(
331             isa => ArrayRef,
332             traits => ['Array'],
333             handles =>{
334             _get_gs_position => 'get',
335             _set_gs_position => 'set',
336             _add_gs_position => 'push',
337             },
338             reader => '_get_all_generic_cache',
339             predicate => '_has_generic_styles_positions'
340             );
341              
342             has _generic_styles_count =>(
343             isa => Int,
344             default => 0,
345             reader => '_get_generic_styles_count',
346             writer => '_set_generic_styles_count',
347             );
348              
349             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
350              
351             sub _build_cell_style_formats{
352 12     12   28 my( $self, $base_ref, $target_header, $exclude_header ) = @_;
353             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
354             ###LogSD $self->get_all_space . '::_build_cell_style_formats', );
355             ###LogSD $phone->talk( level => 'debug', message => [
356             ###LogSD "Building a perl style (cell ready) ref from the base xml ref", $base_ref,
357             ###LogSD ( $target_header ? "..returning only header: $target_header" : undef ),
358             ###LogSD ( $exclude_header ? "..excluding header: $exclude_header" : undef ), ] );
359 12         12 my $return_ref;
360              
361             # Handle target header
362 12 100       29 if( $target_header ){
363 9 50 33     65 if( exists $xml_from_cell->{$target_header} and exists $base_ref->{$xml_from_cell->{$target_header}} ){
    0          
364             ###LogSD $phone->talk( level => 'debug', message => [
365             ###LogSD "Possible conversion of header -$target_header- to: $xml_from_cell->{$target_header}", ] );
366 9         30 $return_ref->{$xml_from_cell->{$target_header}} = $base_ref->{$xml_from_cell->{$target_header}};
367             }elsif( exists $base_ref->{$target_header} ){
368             ###LogSD $phone->talk( level => 'debug', message => [
369             ###LogSD "Using header -$target_header- as is", ] );
370 0         0 $return_ref->{$target_header} = $base_ref->{$target_header};
371             }else{
372 0 0       0 my $alt_header = exists $cell_attributes->{$target_header} ? $cell_attributes->{$target_header} : $target_header;
373 0         0 $self->set_error( "Failed to isolate -$target_header- in the passed ref" );
374 0         0 return { $alt_header => undef };
375             }
376             ###LogSD $phone->talk( level => 'debug', message => [
377             ###LogSD "Updated return ref:", $return_ref ] );
378             }
379              
380             # Handle exclude header
381 12 100       29 if( !$return_ref ){# Handle no target or exclude calls
382             ###LogSD $phone->talk( level => 'debug', message => [
383             ###LogSD "Adding all elements to be filled", ] );
384 3         30 $return_ref = clone( $base_ref );
385             }
386 12 50       30 if( $exclude_header ){
387             ###LogSD $phone->talk( level => 'debug', message => [
388             ###LogSD "Excluding: $exclude_header", ] );
389 0 0 0     0 if( exists $xml_from_cell->{$exclude_header} and exists $return_ref->{$xml_from_cell->{$exclude_header}} ){
    0          
390             ###LogSD $phone->talk( level => 'debug', message => [
391             ###LogSD "Possible conversion of header -$exclude_header- to: $xml_from_cell->{$exclude_header}", ] );
392 0         0 delete $return_ref->{$xml_from_cell->{$exclude_header}};
393             }elsif( exists $base_ref->{$target_header} ){
394             ###LogSD $phone->talk( level => 'debug', message => [
395             ###LogSD "Using header -$exclude_header- as is", ] );
396 0         0 delete $return_ref->{$target_header};
397             }
398             }
399              
400             # Load the sub values
401 12         42 $return_ref = $self->_stack_perl_ref( 'dummy', $return_ref );
402             ###LogSD $phone->talk( level => 'debug', message => [
403             ###LogSD "Returning ref:", $return_ref ] );
404 12         44 return $return_ref;
405             }
406              
407             sub _get_header_and_value{
408 27     27   61 my( $self, $target_header, $target_position ) = @_;
409 27 50       74 $target_header = exists $key_translations->{$target_header} ? $key_translations->{$target_header} : $target_header;
410 27 50       95 my $sub_header = exists $element_lookup->{$target_header} ? $element_lookup->{$target_header} : 'dealers_choice';
411             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
412             ###LogSD $self->get_all_space . '::_get_header_and_value', );
413             ###LogSD $phone->talk( level => 'debug', message => [
414             ###LogSD "getting the ref for target header: $target_header",
415             ###LogSD "..with sub header: $sub_header",
416             ###LogSD "..and position: $target_position", ] );
417              
418              
419 27         28 my( $key, $value );
420 27 50       153 if( $target_header =~ /^apply/ ){
    50          
    50          
421             ###LogSD $phone->talk( level => 'debug', message => [
422             ###LogSD "Found an 'apply flag'", ] );
423 0         0 ( $key, $value ) = ( $target_header, $target_position );
424             }elsif( $target_header eq 'numFmtId' ){
425             ###LogSD $phone->talk( level => 'debug', message => [
426             ###LogSD "Pulling the number conversion for position: $target_position", ] );
427 0         0 ( $key, $value ) = ( $cell_attributes->{$target_header}, $self->get_defined_conversion( $target_position ) );
428             }elsif( !exists $cell_attributes->{$target_header} ){
429 0         0 $self->set_error( "Format key -$target_header- not yet supported by this package" );
430             }else{
431             ###LogSD $phone->talk( level => 'debug', message => [
432             ###LogSD "Reaching into the xml for header -$target_header- position: $target_position", ] );
433 27 50       111 if( is_Int( $target_position ) ){
434 27         249 my $current_node = $self->current_named_node;
435             ###LogSD $phone->talk( level => 'debug', message => [
436             ###LogSD "Starting at node:", $current_node, "..and position: " . ($self->where_am_i//'undef') ] );
437 27         54 my $sub_header = $element_lookup->{$target_header};
438             ###LogSD $phone->talk( level => 'debug', message => [
439             ###LogSD "For the super header: $target_header",
440             ###LogSD "Accessing the Styles file for position -$target_position- of the header: $sub_header",
441             ###LogSD "..currently at the node:", $current_node,] );
442              
443             # Begin at the beginning
444 27         36 my( $result, $node_name, $node_level, $result_ref );
445 27 50       64 if( $current_node->{name} eq $target_header ){
446             ###LogSD $phone->talk( level => 'trace', message =>[
447             ###LogSD "Found the core properties node" ] );
448 0         0 $result = 2;
449 0         0 $node_name = $target_header;
450             }else{
451 27         85 ( $result, $node_name, $node_level, $result_ref ) =
452             $self->advance_element_position( $target_header );# Can't tell which sub position you are at :(
453             ###LogSD $phone->talk( level => 'trace', message =>[
454             ###LogSD "After search arrived at node -$node_name- with result: $result" ] );
455 27 100       87 if( !$result ){# One more attempt
456             ###LogSD $phone->talk( level => 'trace', message =>[
457             ###LogSD "Starting the file over just in case the value is back" ] );
458 14         57 $self->start_the_file_over;
459 14         92 ( $result, $node_name, $node_level, $result_ref ) =
460             $self->advance_element_position( $target_header );
461             ###LogSD $phone->talk( level => 'trace', message =>[
462             ###LogSD "After search arrived at node -$node_name- with result: $result" ] );
463             }
464 27 50       89 if( !$result ){# Just not here!
465             ###LogSD $phone->talk( level => 'trace', message =>[ "Fail!!!!!!!!!!" ] );
466 0         0 $self->set_error( "Requested styles header -$target_header- is not found in this workbook" );
467 0         0 return( undef, undef );
468             }
469             }
470              
471             # Index to the indicated sub position
472 27         116 ( $result, $node_name, $node_level, $result_ref ) =
473             $self->advance_element_position( $sub_header, $target_position + 1 );
474             ###LogSD $phone->talk( level => 'trace', message =>[
475             ###LogSD "After search for header -$sub_header- and position -" .
476             ###LogSD ($target_position + 1) . "- arrived at node -$node_name- with result: $result" ] );
477 27 50       88 if( !$result ){
478 0         0 $self->set_error( "Requested styles sub position for -$target_header- is not found in this workbook" );
479 0         0 return( undef, undef );
480             }
481              
482             # Pull the data
483 27         100 my $base_ref = $self->parse_element;
484             ###LogSD $phone->talk( level => 'debug', message => [
485             ###LogSD "Pulling data from target header -$target_header- for position -$target_position- gives ref:", $base_ref ] );
486              
487 27         204 ( $key, $value ) = ( $cell_attributes->{$target_header}, $base_ref );
488             }else{
489             ###LogSD $phone->talk( level => 'debug', message => [
490             ###LogSD "just translating the key for the sub-ref:", $target_position ] );
491 0         0 ( $key, $value ) = ( $cell_attributes->{$key}, $target_position );
492             }
493             }
494 27         105 $value = $self->squash_node( $value );
495             ###LogSD $phone->talk( level => 'debug', message => [
496             ###LogSD "Returning key: $key", "..and value:", $value ] );
497 27         94 return( $key, $value );
498             }
499              
500             sub _stack_perl_ref{
501 367     367   390 my( $self, $top_ref, $current_ref, ) = @_;
502             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
503             ###LogSD $self->get_all_space . '::_stack_perl_ref', );
504             ###LogSD $phone->talk( level => 'trace', message => [
505             ###LogSD "..with high level ref:", $top_ref ] );
506             ###LogSD $phone->talk( level => 'debug', message => [
507             ###LogSD "building out the ref:", $current_ref ] );
508 367         288 my $new_ref;
509              
510             # Handle attributes
511 367 50       644 if( is_HashRef( $current_ref ) ){
    0          
512             ###LogSD $phone->talk( level => 'trace', message => [
513             ###LogSD "processing the attributes of a hashref" ] );
514 367         1655 for my $attribute ( keys %$current_ref ){
515             ###LogSD $phone->talk( level => 'debug', message => [
516             ###LogSD "Processing the attribute $attribute => $current_ref->{$attribute}", ] );
517 2230 100 66     128570 if( $attribute eq 'xfId' or $attribute eq 'builtinId' ){
    100          
    100          
    100          
518             ###LogSD $phone->talk( level => 'debug', message => [
519             ###LogSD "Skipping the -$attribute- attribute", ] );
520             }elsif( $attribute eq 'numFmtId' ){
521             ###LogSD $phone->talk( level => 'debug', message => [
522             ###LogSD "Setting -$cell_attributes->{$attribute}- with number conversion for position: $current_ref->{$attribute}", ] );
523 361         1094 $new_ref->{$cell_attributes->{$attribute}} = $self->get_defined_conversion( $current_ref->{$attribute} );
524             }elsif( $attribute =~ /Id$/i ){
525             ###LogSD $phone->talk( level => 'debug', message => [
526             ###LogSD "pulling sub ref for attribute -$attribute- from: $key_translations->{$attribute}", substr($attribute, 0, -2) ] );
527 1080         745 my( $key, $return );
528 1080 100       26769 if( $self->should_cache_positions ){
529             ###LogSD $phone->talk( level => 'trace', message => [
530             ###LogSD "Positions should be cached (built) already - using top ref:", $top_ref ] );
531 1065         1233 my $sub_node = $top_ref->{$key_translations->{$attribute}};
532 1065 100       1365 my $count = exists $sub_node->{count} ? $sub_node->{count} : undef ;# to be used for double checking as needed
533 1065 100       1582 my $list_ref = exists $sub_node->{list} ? $sub_node->{list} : [$sub_node->{substr($attribute, 0, -2)}] ;
534             ###LogSD $phone->talk( level => 'debug', message => [
535             ###LogSD "Pulling position -$current_ref->{$attribute}- from sub ref:", $sub_node, $list_ref ] );
536 1065         1560 $return = $list_ref->[$current_ref->{$attribute}];
537 1065         1472 $key = $cell_attributes->{$attribute};
538             }else{
539             ###LogSD $phone->talk( level => 'debug', message => [
540             ###LogSD "Positions are not cached - pulling the data from subsection " .
541             ###LogSD "-$key_translations->{$attribute}- and position: $current_ref->{$attribute}", ] );
542 15         54 ( $key, $return ) = $self->_get_header_and_value( $key_translations->{$attribute}, $current_ref->{$attribute} );
543             }
544             ###LogSD $phone->talk( level => 'debug', message => [
545             ###LogSD "Setting the new ref attribute -$key- based on the base attribute -$attribute- as the cell value:", $return ] );
546 1080         1643 $new_ref->{$key} = $return;
547             }elsif( exists $cell_attributes->{$attribute} ){
548             ###LogSD $phone->talk( level => 'debug', message => [
549             ###LogSD "Setting new ref key -$cell_attributes->{$attribute}- to value: $current_ref->{$attribute}", ] );
550 77         168 $new_ref->{$cell_attributes->{$attribute}} = $current_ref->{$attribute};
551             }else{
552             ###LogSD $phone->talk( level => 'debug', message => [
553             ###LogSD "Setting new ref key -$attribute- to value: $current_ref->{$attribute}", ] );
554 381         636 $new_ref->{$attribute} = $current_ref->{$attribute};
555             }
556             }
557             }elsif( defined $current_ref ){
558             ###LogSD $phone->talk( level => 'debug', message => [
559             ###LogSD "The attribute only has a base value: $current_ref->{attributes}" ] );
560 0         0 $new_ref = $current_ref;
561             }
562              
563             ###LogSD $phone->talk( level => 'trace', message => [
564             ###LogSD "Final new ref is:", $new_ref ] );
565 367         17386 return $new_ref;
566             }
567              
568             #########1 Phinish 3#########4#########5#########6#########7#########8#########9
569              
570 20     20   114 no Moose::Role;
  20         37  
  20         113  
571              
572             1;
573              
574             #########1 Documentation 3#########4#########5#########6#########7#########8#########9
575             __END__
576              
577             =head1 NAME
578              
579             Spreadsheet::Reader::ExcelXML::PositionStyles - Position based styles reader
580              
581             =head1 SYNOPSYS
582              
583             !!!! Example code - will not run standalone !!!!
584              
585             use MooseX::ShortCut::BuildInstance qw( build_instance );
586             use Spreadsheet::Reader::ExcelXML::XMLReader::PositionStyles;
587             use Spreadsheet::Reader::ExcelXML::XMLReader;
588             my $test_instance = build_instance(
589             package => 'StylesInterface',
590             superclasses => ['Spreadsheet::Reader::ExcelXML::XMLReader'],
591             add_roles_in_sequence => [
592             'Spreadsheet::Reader::ExcelXML::XMLReader::PositionStyles',
593             ],
594             file => 'styles.xml',
595             workbook_inst => $workbook_instance,<--- Built elswhere!!!
596             );
597              
598             =head1 DESCRIPTION
599              
600             This role is written to provide the methods 'get_format' and 'get_default_format' for
601             the styles file reading where the styles file elements are called out by position.
602             The usually occurs in the case where and .xlsx file (zipped format) is provided.
603              
604             =head2 Requires
605              
606             These are the methods required by this role and their default provider. All
607             methods are imported straight across with no re-naming.
608              
609             =over
610              
611             L<Spreadsheet::Reader::Format::ParseExcelFormatStrings/get_defined_conversion( $position )>
612              
613             L<Spreadsheet::Reader::Format::FmtDefault/set_defined_excel_formats( %args )>
614              
615             L<Spreadsheet::Reader::ExcelXML::XMLReader/good_load>
616              
617             L<Spreadsheet::Reader::ExcelXML::XMLReader/start_the_file_over>
618              
619             L<Spreadsheet::Reader::ExcelXML::XMLReader/close_the_file>
620              
621             L<Spreadsheet::Reader::ExcelXML::XMLReader/advance_element_position( $element, [$iterations] )>
622              
623             L<Spreadsheet::Reader::ExcelXML::XMLReader/parse_element>
624              
625             L<Spreadsheet::Reader::ExcelXML::XMLReader/current_named_node>
626              
627             L<Spreadsheet::Reader::ExcelXML::XMLReader/squash_node( $node )>
628              
629             L<Spreadsheet::Reader::ExcelXML::Error/set_error( $error_string )>
630              
631             =back
632              
633             =head2 Method(s)
634              
635             These are the methods provided by this role.
636              
637             =head3 get_format( $position, [$header], [$exclude_header] )
638              
639             =over
640              
641             B<Definition:> This will return the styles information from the identified $position
642             (counting from zero). The target position is usually drawn from the cell data stored in
643             the worksheet. The information is returned as a perl hash ref. Since the styles data
644             is in two tiers it finds all the subtier information for each indicated piece and appends
645             them to the hash ref as values for each type key.
646              
647             B<Accepts position 0:> $position = an integer for the styles $position.
648              
649             B<Accepts position 1:> $header = the target header key (use the
650             L<Spreadsheet::Reader::ExcelXML::Cell/Attributes> that are cell formats as the definition
651             of range for this.) It will cause only this header subset to be returned
652              
653             B<Accepts position 2:> $exclude_header = the target header key (use the
654             L<Spreadsheet::Reader::ExcelXML::Cell/Attributes> that are cell formats as the definition
655             of range for this.) It will exclude the header from the returned data set.
656              
657             B<Returns:> a hash ref of data
658              
659             =back
660              
661             =head3 get_default_format( [$header], [$exclude_header] )
662              
663             =over
664              
665             B<Definition:> For any cell that does not have a unquely identified format excel generally
666             stores a default format for the remainder of the sheet. This will return the two
667             tiered default styles information. The information is returned in the same format as the
668             get_format method.
669              
670             B<Accepts position 0:> $header = the target header key (use the
671             L<Spreadsheet::Reader::ExcelXML::Cell/Attributes> that are cell formats as the definition
672             of range for this.) It will cause only this header subset to be returned
673              
674             B<Accepts position 1:> $exclude_header = the target header key (optional at position 2) (use the
675             L<Spreadsheet::Reader::ExcelXML::Cell/Attributes> that are cell formats as the definition
676             of range for this.) It will exclude the header from the returned data set.
677              
678             B<Returns:> a hash ref of data
679              
680             =back
681              
682             =head3 load_unique_bits
683              
684             =over
685              
686             B<Definition:> When the xml file first loads this is available to pull customized data.
687             It mostly pulls metadata and stores it in hidden attributes for use later. If all goes
688             according to plan it sets L<Spreadsheet::Reader::ExcelXML::XMLReader/good_load> to 1.
689              
690             B<Accepts:> Nothing
691              
692             B<Returns:> Nothing
693              
694             =back
695              
696             =head2 Attributes
697              
698             Data passed to new when creating an instance with this role. For
699             modification of this(ese) attribute(s) see the listed 'attribute
700             methods'. For more information on attributes see
701             L<Moose::Manual::Attributes>. The easiest way to modify this(ese)
702             attribute(s) is during instance creation before it is passed to the
703             workbook or parser.
704              
705             =head3 cache_positions
706              
707             =over
708              
709             B<Definition:> Especially for sheets with lots of stored formats the
710             parser can slow way down when accessing each postion. This is
711             because the are not stored sequentially and the reader is a JIT linear
712             parser. To go back it must restart and index through each position till
713             it gets to the right place. This is especially true for excel sheets
714             that have experienced any significant level of manual intervention prior
715             to being read. This attribute sets caching (default on) for styles
716             so the parser builds and stores all the styles settings at the beginning.
717             If the file is cached it will close and release the file handle in order
718             to free up some space. (a small win in exchange for the space taken by
719             the cache).
720              
721             B<Default:> 1 = caching is on
722              
723             B<Range:> 1|0
724              
725             B<Attribute required:> yes
726              
727             B<attribute methods> Methods provided to adjust this attribute
728              
729             =over
730              
731             none - (will be autoset by L<Spreadsheet::Reader::ExcelXML/cache_positions>)
732              
733             =back
734              
735             =back
736              
737             =head1 SUPPORT
738              
739             =over
740              
741             L<github Spreadsheet::Reader::ExcelXML/issues
742             |https://github.com/jandrew/p5-spreadsheet-reader-excelxml/issues>
743              
744             =back
745              
746             =head1 TODO
747              
748             =over
749              
750             B<1.> Nothing yet
751              
752             =back
753              
754             =head1 AUTHOR
755              
756             =over
757              
758             =item Jed Lund
759              
760             =item jandrew@cpan.org
761              
762             =back
763              
764             =head1 COPYRIGHT
765              
766             This program is free software; you can redistribute
767             it and/or modify it under the same terms as Perl itself.
768              
769             The full text of the license can be found in the
770             LICENSE file included with this module.
771              
772             This software is copyrighted (c) 2016 by Jed Lund
773              
774             =head1 DEPENDENCIES
775              
776             =over
777              
778             L<Spreadsheet::Reader::ExcelXML> - the package
779              
780             =back
781              
782             =head1 SEE ALSO
783              
784             =over
785              
786             L<Spreadsheet::Read> - generic Spreadsheet reader
787              
788             L<Spreadsheet::ParseExcel> - Excel binary version 2003 and earlier (.xls files)
789              
790             L<Spreadsheet::XLSX> - Excel version 2007 and later
791              
792             L<Spreadsheet::ParseXLSX> - Excel version 2007 and later
793              
794             L<Log::Shiras|https://github.com/jandrew/Log-Shiras>
795              
796             =over
797              
798             All lines in this package that use Log::Shiras are commented out
799              
800             =back
801              
802             =back
803              
804             =cut
805              
806             #########1#########2 main pod documentation end 5#########6#########7#########8#########9