File Coverage

lib/Spreadsheet/Reader/ExcelXML/XMLReader.pm
Criterion Covered Total %
statement 501 515 97.2
branch 202 228 88.6
condition 94 110 85.4
subroutine 40 40 100.0
pod 11 12 91.6
total 848 905 93.7


line stmt bran cond sub pod time code
1             package Spreadsheet::Reader::ExcelXML::XMLReader;
2             our $AUTHORITY = 'cpan:JANDREW';
3 39     39   21282792 use version; our $VERSION = version->declare('v0.16.8');
  39         64  
  39         288  
4             ###LogSD warn "You uncovered internal logging statements for Spreadsheet::Reader::ExcelXML::XMLReader-$VERSION";
5              
6 39     39   4166 use 5.010;
  39         103  
7 39     39   144 use Moose;
  39         51  
  39         249  
8 39     39   184239 use MooseX::StrictConstructor;
  39         151579  
  39         244  
9 39     39   138725 use MooseX::HasDefaults::RO;
  39         67002  
  39         270  
10             #~ use Text::ParseWords 3.27;
11 39         436 use Types::Standard qw(
12             Int Bool Enum Num
13             Str ArrayRef is_ArrayRef is_HashRef
14             is_Int HashRef
15 39     39   167424 );
  39         239728  
16 39     39   45911 use Carp qw( confess longmess );
  39         57  
  39         2352  
17 39     39   160 use Clone qw( clone );
  39         56  
  39         1298  
18 39     39   147 use Data::Dumper;
  39         49  
  39         1580  
19 39     39   7877 use Encode qw( encode decode );
  39         112148  
  39         1839  
20 39     39   4835 use IO::Handle;
  39         48573  
  39         1155  
21 39     39   12179 use FileHandle;
  39         39325  
  39         217  
22 39     39   11792 use lib '../../../../lib',;
  39         51  
  39         283  
23             ###LogSD with 'Log::Shiras::LogSpace';
24 2     1   1665 ###LogSD use Log::Shiras::Telephone;
  2         3185  
  1         23  
25 39     39   11003 use Spreadsheet::Reader::ExcelXML::Types qw( IOFileType );
  39         93  
  39         708  
26              
27             #########1 Public Attributes 3#########4#########5#########6#########7#########8#########9
28              
29             has file =>(
30             isa => IOFileType,
31             reader => 'get_file',
32             writer => 'set_file',
33             predicate => 'has_file',
34             clearer => 'clear_file',
35             coerce => 1,
36             trigger => \&_start_xml_reader,
37             handles => [qw( close getline seek binmode )],
38             );
39              
40             has workbook_inst =>(
41             isa => 'Spreadsheet::Reader::ExcelXML::Workbook',
42             writer => 'set_workbook_inst',
43             predicate => '_has_workbook_inst',
44             handles => [qw(
45             get_group_return_type set_error get_defined_conversion
46             set_defined_excel_formats parse_excel_format_string counting_from_zero
47             are_spaces_empty get_shared_string has_shared_strings_interface
48             should_skip_hidden spreading_merged_values starts_at_the_edge
49             get_empty_return_type get_values_only get_epoch_year
50             change_output_encoding get_error_inst has_styles_interface
51             boundary_flag_setting is_empty_the_end get_format
52             get_rel_info get_sheet_info get_sheet_names
53             collecting_merge_data collecting_column_formats
54             )],# The regex import doesn't work here due to the twistiness of the overall package
55             );
56              
57             has xml_version =>(
58             isa => Num,
59             reader => 'version',
60             writer => '_set_xml_version',
61             clearer => '_clear_xml_version',
62             predicate => '_has_xml_version',
63             );
64              
65             has xml_encoding =>(
66             isa => Str,
67             reader => 'encoding',
68             predicate => 'has_encoding',
69             writer => '_set_xml_encoding',
70             clearer => '_clear_xml_encoding',
71             );
72              
73             has xml_progid =>(
74             isa => Str,
75             reader => 'progid',
76             predicate => 'has_progid',
77             writer => '_set_xml_progid',
78             clearer => '_clear_xml_progid',
79             );
80              
81             has xml_header =>(
82             isa => Str,
83             reader => 'get_header',
84             writer => '_set_xml_header',
85             predicate => '_has_xml_header',
86             clearer => '_clear_xml_header',
87             );
88              
89             has xml_doctype =>(
90             isa => HashRef,
91             reader => 'doctype',
92             predicate => 'has_doctype',
93             writer => '_set_xml_doctype',
94             clearer => '_clear_xml_doctype',
95             );
96              
97             has position_index =>(
98             isa => Int,
99             reader => 'where_am_i',
100             writer => 'i_am_here',
101             clearer => 'clear_location',
102             predicate => 'has_position',
103             );
104              
105             has file_type =>(
106             isa => Enum[ 'xml' ],
107             reader => 'get_file_type',
108             default => 'xml',
109             );
110              
111             has stacking =>(
112             isa => Bool,
113             reader => 'should_be_stacking',
114             writer => 'change_stack_storage_to',
115             default => 1,
116             );
117              
118             #########1 Public Methods 3#########4#########5#########6#########7#########8#########9
119              
120             sub start_the_file_over{
121 152     152 1 4654 my( $self ) = @_;
122             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
123             ###LogSD $self->get_all_space . '::XMLReader::start_the_file_over', );
124             ###LogSD $phone->talk( level => 'debug', message =>[
125             ###LogSD "arrived at start_the_file_over" ] );# , caller( 0 ), caller( 1 ), caller( 2 ),
126              
127             # Clear current settings
128 152         4470 $self->clear_location;
129 152         6239 $self->_set_node_stack( [] );
130 152         8361 $self->_set_ref_stack( [] );
131 152         4053 $self->_set_string_stack( [] );
132 152         4064 $self->_set_position_stack( [] );
133 152         3977 $self->change_stack_storage_to( 1 );
134              
135             # Start at the beginning
136 152         1108 $self->seek(0, 0);
137             ###LogSD $phone->talk( level => 'debug', message =>[ "The object is reset" ] );
138              
139             #start reading
140 152         2670 $self->_read_file;
141             ###LogSD $phone->talk( level => 'debug', message =>[ "Arrived at the first node" ] );
142              
143 152         4235 return $self->not_end_of_file;
144             }
145              
146             #~ sub is_end_of_file{
147             #~ my( $self ) = @_;
148             #~ ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
149             #~ ###LogSD $self->get_all_space . '::XMLReader::reached_end_of_file', );
150             #~ return !$self->has_nodes;
151             #~ }
152              
153             sub parse_element{
154 989     987 1 1321 my ( $self, $level, ) = @_;# $attribute_ref
155             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
156             ###LogSD $self->get_all_space . '::XMLReader::parse_element', );
157             ###LogSD $phone->talk( level => 'debug', message =>[
158             ###LogSD "Parsing current element", (defined $level ? "..to depth: $level" : undef), ] );###LogSD (defined $attribute_ref ? "..with attribute_ref:" : undef), $attribute_ref
159              
160             # Check for end of file state
161 989 100       30156 if( !$self->not_end_of_file ){
162             ###LogSD $phone->talk( level => 'debug', message =>[ "Reached end of file" ] );
163 9         4690 return 'EOF';
164             }
165              
166             # Store stacking state and then ensure it is on
167 986         33922 my $stacking_state = $self->should_be_stacking;
168 980         23747 $self->change_stack_storage_to( 1 );
169              
170             # Check for self contained node
171 980         2163 my $current_node = clone( $self->current_named_node );
172             ###LogSD $phone->talk( level => 'debug', message =>[ "Current node is:", $current_node ] );
173 984 100       2696 if( $current_node->{closed} eq 'closed' ){
174             ###LogSD $phone->talk( level => 'debug', message =>[ "Found a self contained node: ", $current_node ] );
175 189         469 map{ delete $current_node->{$_} } qw( name type closed initial_string );# level
  744         895  
176 189         544 $self->_build_out_the_return( [ $current_node ] );
177              
178             # pull the compiled ref for return
179 189         11756 my $built_reference = $self->_remove_ref;
180             ###LogSD $phone->talk( level => 'trace', message =>[
181             ###LogSD "Final result result:", $built_reference ] );
182 185         4420 $self->_set_ref_stack( [] );
183 185         4663 $self->_set_position_stack( [] );
184              
185 185         589 return $built_reference;
186             }
187              
188             # Build target name and level
189 795         1663 my( $target_node, $target_level ) = @$current_node{ qw( name level ) };
190 795 100       1449 $target_level = defined $level ? ($target_level + $level) : undef;
191             ###LogSD $phone->talk( level => 'debug', message =>[
192             ###LogSD "Target node is: $target_node",
193             ###LogSD (defined $target_level ? "..and target level is: $target_level" : undef ) ] );
194              
195             # Cycle to the bottom and back up
196 795         734 my $done;
197 795         1657 while( !$done ){
198 7636         10946 my( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
199             ###LogSD $phone->talk( level => 'debug', message =>[
200             ###LogSD "Node read returned: $result_type",
201             ###LogSD "..up to node named: $top_node_name",
202             ###LogSD "..at level: $top_node_level", $result ] );
203              
204             #Handle unexpected EOF
205 7636 100       12604 if( !$result_type ){
206 6         24 return 'EOF';
207             }
208              
209             # Handle any rewind and dump
210 7638 100       14113 if( scalar( @$result ) > 0 ){
211             ###LogSD $phone->talk( level => 'debug', message =>[
212             ###LogSD "Reached the bottom of something",
213             ###LogSD "..checking if: $target_node",
214             ###LogSD "..equals: " . $result->[-1]->{name} ] );
215              
216             # Check if you reached the top
217 4540 100       7588 if( $result->[-1]->{name} eq $target_node ){
218             ###LogSD $phone->talk( level => 'debug', message =>[
219             ###LogSD "received the very last return" ] );
220 797         7172 $done = 1;
221 797         895 my $top_ref = pop @$result;
222             ###LogSD $phone->talk( level => 'debug', message =>[
223             ###LogSD "getting rid of (most) of the ref:", $top_ref ] );
224 806         1180 map{ delete $top_ref->{$_} } qw( name type closed initial_string );# level
  3185         3955  
225 806 100       28987 if( keys %$top_ref ){
226             ###LogSD $phone->talk( level => 'debug', message =>[
227             ###LogSD "Still something left in top ref:", $top_ref, "..so adding it back result:", $result ] );
228             #~ if( $result->[-1] ){ # Add the keys to the top ref as elements to the next node down
229             #~ map{ $result->[-1]->{$_} = $top_ref->{$_} } keys %$top_ref;
230             #~ }else{
231 793         843 push @$result, $top_ref;
232             #~ }
233             ###LogSD $phone->talk( level => 'debug', message =>[
234             ###LogSD "Updated result:", $result ] );
235             }
236             }
237              
238             # remove results below a certain level
239 4549   100     8731 while( defined $target_level and defined $result->[0] and
      100        
240             $result->[0]->{level} > $target_level ){
241 24         132 my $unused = shift @$result;
242             ###LogSD $phone->talk( level => 'trace', message =>[
243             ###LogSD "Throwing away:", $unused ] );
244             }
245              
246             # Build out the return
247             ###LogSD $phone->talk( level => 'trace', message =>[
248             ###LogSD "Building out the result:", $result ] );
249 4545         23324 $self->_build_out_the_return( $result, );
250             }
251             }
252              
253             # pull the compiled ref for return
254 797         20281 my $built_reference = $self->_remove_ref;
255             ###LogSD $phone->talk( level => 'trace', message =>[
256             ###LogSD "Final result result:", $built_reference ] );
257 797         26842 $self->_set_ref_stack( [] );
258 797         19570 $self->_set_position_stack( [] );
259              
260             # restore stacking state
261 797         19199 $self->change_stack_storage_to( $stacking_state );
262              
263 797         11597 return $built_reference;
264             }
265              
266             sub advance_element_position{
267 1164     1148 1 40149 my ( $self, $element, $position ) = @_;
268 1152 100 66     2950 if( $position and $position < 1 ){
269 4         26 confess "You can only advance element position in a positive direction, |$position| is not correct.";
270             }
271 1152   100     12588 $position ||= 1;
272             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
273             ###LogSD $self->get_all_space . '::XMLReader::advance_element_position', );
274             ###LogSD $phone->talk( level => 'info', message => [
275             ###LogSD "Advancing to element -" . ($element//'') . "- -$position- times", ] );
276              
277             # Check for end of file and opt out
278 1152 100       32344 if( !$self->not_end_of_file ){
279             ###LogSD $phone->talk( level => 'debug', message =>[
280             ###LogSD "Already at the EOF - returning failure", ] );
281 16         16427 return undef;
282             }
283              
284 1147         12415 my( $result, $destination_name, $destination_level, $level_ref);
285 1141         1078 my $x = 0;
286 1150         2238 for my $y ( 1 .. $position ){
287             ###LogSD $phone->talk( level => 'debug', message => [
288             ###LogSD "Advancing position iteration: $y",
289             ###LogSD "Searching for element: " . ($element//'(next)'), ] );
290 1264 100       24674 ($result, $destination_name, $destination_level, $level_ref) = defined $element ?
291             $self->_next_element( $element ) :
292             $self->_next_unnamed_element;
293             ###LogSD $phone->talk( level => 'debug', message => [
294             ###LogSD "search result: " . ($result//'none'),
295             ###LogSD "arrived at node named: $destination_name",
296             ###LogSD ( defined( $destination_level ) ? "..and node level: $destination_level" : undef), $level_ref ] );
297             #~ if( $element and $result == 1 ){# Advance passed a closing node
298             #~ ###LogSD $phone->talk( level => 'debug', message =>[ "Handle closing node", ] );
299             #~ ($result, $destination_name, $destination_level, $level_ref) = $self->_next_element( $element );
300             #~ ###LogSD $phone->talk( level => 'debug', message => [
301             #~ ###LogSD "search result: " . ($result//'none'),
302             #~ ###LogSD "arrived at node named: $destination_name",
303             #~ ###LogSD ( defined( $destination_level ) ? "..and node level: $destination_level" : undef), $level_ref ] );
304             #~ }
305 1259 100       2520 last if !$result;
306 1141         1443 $x++;
307             ###LogSD $phone->talk( level => 'debug', message => [
308             ###LogSD "Successfully indexed -$x- times for position request: $position", ] );
309             }
310              
311             ###LogSD $phone->talk( level => 'debug', message => [
312             ###LogSD "returning result: " . ($x==$position), ] );
313 1151 100       14382 return (($element ? ($destination_name eq $element) : $result), $destination_name, $destination_level, $level_ref);
314             }
315              
316             sub next_sibling{ # should land on a new node (or EOF)
317 62     52 1 14888 my ( $self, ) = @_;
318             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
319             ###LogSD $self->get_all_space . '::XMLReader::next_sibling', );
320             ###LogSD $phone->talk( level => 'info', message => [
321             ###LogSD "Advancing to the next sibling", ] );
322              
323             # Check for end of file and opt out
324 62 100       1923 if( !$self->not_end_of_file ){
325             ###LogSD $phone->talk( level => 'debug', message =>[
326             ###LogSD "Already at the EOF - returning failure", ] );
327 10         10501 return undef;
328             }
329              
330             # Find target level
331 58         117 my $target_level = $self->current_named_node->{level};
332             ###LogSD $phone->talk( level => 'debug', message =>[
333             ###LogSD "Traversing to the next start node at level: $target_level" ] );
334              
335 58         155 my ( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
336             ###LogSD $phone->talk( level => 'debug', message =>[
337             ###LogSD "Read file result type -$result_type- at level -$top_node_level- with node name: $top_node_name" ] );
338 58   100     9649 while( $result_type == 1 or $top_node_level > $target_level ){
339             ###LogSD $phone->talk( level => 'debug', message =>[
340             ###LogSD "Still looking for the target level" ] );
341 1134         11212 ( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
342             ###LogSD $phone->talk( level => 'debug', message =>[
343             ###LogSD "Read file result type -$result_type- at level -$top_node_level- with node name: $top_node_name" ] );
344             #~ ###LogSD $phone->talk( level => 'trace', message =>[
345             #~ ###LogSD "Read file result type -$result_type- at with result: ", ($result//'fail') ] );
346 1128 100       4564 last if $result_type == 0;
347             }
348              
349             ###LogSD $phone->talk( level => 'debug', message =>[
350             ###LogSD "Target node level -$target_level- search resulted in:", $self->current_named_node ] );
351 52         169 return( ($top_node_level == $target_level), $top_node_name, $top_node_level, $result);
352             }
353              
354             sub skip_siblings{ # should land on a new node?? (or EOF)
355 1     1 1 2 my ( $self, ) = @_;
356             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
357             ###LogSD $self->get_all_space . '::XMLReader::skip_siblings', );
358             ###LogSD $phone->talk( level => 'info', message => [
359             ###LogSD "Advancing past the remaining siblings", ] );
360              
361             # Check for end of file and opt out
362 1 50       34 if( !$self->not_end_of_file ){
363             ###LogSD $phone->talk( level => 'debug', message =>[
364             ###LogSD "Already at the EOF - returning failure", ] );
365 0         0 return undef;
366             }
367              
368             # Find target level
369 1         3 my $target_level = $self->current_named_node->{level} - 1;
370             ###LogSD $phone->talk( level => 'debug', message =>[
371             ###LogSD "Traversing to the next start node at level: $target_level",
372             ###LogSD "(Which is one level up from the current level)" ] );
373              
374 1         5 my ( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
375 1   100     8 while( $result_type == 1 or $top_node_level > $target_level ){
376             ###LogSD $phone->talk( level => 'debug', message =>[
377             ###LogSD "Still looking for the target level" ] );
378 6         10 ( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
379             }
380              
381             ###LogSD $phone->talk( level => 'debug', message =>[
382             ###LogSD "Target node level -$target_level- search resulted in: $result", ] );
383 1         6 return(( $top_node_level == $target_level ), $top_node_name, $top_node_level, $result );
384             }
385              
386             sub current_named_node{
387 1162     1162 1 1287 my( $self, $element ) = @_;
388             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
389             ###LogSD $self->get_all_space . '::XMLReader::current_named_node', );
390             ###LogSD $phone->talk( level => 'debug', message => [
391             ###LogSD "searching for the current value node", $self->_get_node_stack ] );# caller( 0 ), caller( 1 ), caller( 2 )
392 1162         30593 my $current_node = $self->_current_node;
393             ###LogSD $phone->talk( level => 'debug', message => [
394             ###LogSD "The current node is:", $current_node ] );
395 1162 100 100     10962 if( $current_node and $current_node->{name} eq 'raw_text' ){# Add the following if you want to search to the text node level -> and $current_node =~ /^\s+$/
396             ###LogSD $phone->talk( level => 'debug', message => [
397             ###LogSD "The last node is a text node" ] );
398 86         17565 $current_node = $self->_prior_node;
399             }
400             ###LogSD $phone->talk( level => 'info', message => [
401             ###LogSD "The final current node is:", $current_node ] );
402              
403 1162         12491 return $current_node;
404             }
405              
406             sub squash_node{
407 5395     5389 1 34393 my( $self, $ref, ) = @_;
408             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
409             ###LogSD $self->get_all_space . '::XMLReader::squash_node', );
410             ###LogSD $phone->talk( level => 'debug', message => [
411             ###LogSD "reducing the xml-style node to a perl-style data structure:", $ref,] );# caller( 1 )
412 5391         3703 my $perl_node;
413 5391         22022 my $success = 0;
414 5395         32662 my $is_a_list = 0;
415 5395         14652 my ( $list_ref, $hash_ref, $attribute_ref );
416              
417             # Handle the unsquashable
418 5389 100 66     11716 if( !$ref or !is_HashRef( $ref ) ){
419             ###LogSD $phone->talk( level => 'debug', message => [
420             ###LogSD "The ref is unsquashable: " . ($ref//'undef')] );
421 20         44 return( 1, $ref);
422             }
423              
424             # Build any sub lists
425 5383 100       17160 if( exists $ref->{list_keys} ){
426 3119         2337 $success = 1;
427 3119         2205 my $x = 0;
428 3133         2325 for my $key ( @{$ref->{list_keys}} ){
  3125         4179  
429             ###LogSD $phone->talk( level => 'debug', message => [
430             ###LogSD "Processing the node for key -$key- from:", $ref->{list}->[$x],] );
431             my $sub_value =
432             !defined $ref->{list}->[$x] ? undef :
433             ( is_HashRef( $ref->{list}->[$x] ) and
434             ( exists $ref->{list}->[$x]->{list} or
435             exists $ref->{list}->[$x]->{attributes} or
436             exists $ref->{list}->[$x]->{val} ) ) ?
437             $self->squash_node( $ref->{list}->[$x] ) :
438             ( is_HashRef( $ref->{list}->[$x] ) and
439             scalar( keys %{$ref->{list}->[$x]} ) == 1 and
440             exists $ref->{list}->[$x]->{raw_text} ) ?
441 5602 100 66     10578 $ref->{list}->[$x]->{raw_text} : $ref->{list}->[$x];
    100 66        
    100          
442 5602 100       8784 if( $key eq 'attributes' ){
443 494         527 $attribute_ref = $sub_value;
444             }else{
445 5118 100 100     16936 $is_a_list = 1 if exists $hash_ref->{$key} and length( $key ) > 0;
446 5118         23042 $list_ref->[$x] = $sub_value;
447 5118         3613 $x++;
448             }
449 5598 100       11671 $hash_ref->{$key} = $sub_value if !$is_a_list;
450             ###LogSD $phone->talk( level => 'trace', message => [
451             ###LogSD "Perl alt nodes with key -$key- added:", $hash_ref, $list_ref, $attribute_ref ] );
452             }
453 3127         3788 delete $ref->{list_keys};
454 3127         3747 delete $ref->{list};
455             }
456              
457             # Add the attributes
458 5377 100       7154 if( exists $ref->{attributes} ){
459 2850 100       4021 $perl_node = $is_a_list ? { attributes => $ref->{attributes} } : $ref->{attributes};
460             ###LogSD $phone->talk( level => 'debug', message => [
461             ###LogSD "Perl node with attributes added:", $perl_node,] );
462 2850         2627 delete $ref->{attributes};
463 2846         14860 $success = 1;
464             }
465              
466             # Check for a 'val' key (meaning the ref really just stores one value)
467             ###LogSD $phone->talk( level => 'trace', message => [
468             ###LogSD "Performing the 'val' test with success => $success and ref:", $ref,] );
469 5375 100 100     7424 if( !$success and exists $ref->{val} ){
470             ###LogSD $phone->talk( level => 'debug', message => [
471             ###LogSD "Found a node with 'val': $ref->{val}",] );
472 570         857 return( 1, $ref->{val} );
473             }
474              
475             # Check for a 'raw_text' node (xml raw_text nodes)
476             ###LogSD $phone->talk( level => 'trace', message => [
477             ###LogSD "Performing the 'raw_text' test with success => $success and ref:", $ref,] );
478 4821 50 66     5878 if( !$success and exists $ref->{raw_text} ){
479             ###LogSD $phone->talk( level => 'debug', message => [
480             ###LogSD "Found a node with 'raw_text': $ref->{raw_text}",] );
481 10         55 return( 1, $ref );
482             }
483              
484             # Select the list or hash choice
485 4819 100       19497 if( $is_a_list ){
486             ###LogSD $phone->talk( level => 'debug', message => [
487             ###LogSD "Using the list preference" ] );
488 311         533 $perl_node->{list} = $list_ref;
489 317 100       825 $perl_node->{attributes} = $attribute_ref if $attribute_ref;
490             }else{
491             ###LogSD $phone->talk( level => 'debug', message => [
492             ###LogSD "Using the hash preference:", $hash_ref ] );
493 4522 100       32868 if( exists $hash_ref->{attributes} ){
494             ###LogSD $phone->talk( level => 'debug', message => [
495             ###LogSD "found a built attributes key" ] );
496 316         569 my $attribute_node = $self->squash_node( $hash_ref );
497 316         335 delete $hash_ref->{attributes};
498 330         638 map{ $perl_node->{$_} = $attribute_node->{$_} } keys % $attribute_node;
  822         26213  
499             }
500             #~ else{
501 4508         6638 map{ $perl_node->{$_} = $hash_ref->{$_} } keys %$hash_ref;
  3832         5934  
502             #~ }
503 4522         3887 $success = 1;
504             }
505             ###LogSD $phone->talk( level => 'trace', message => [
506             ###LogSD "Returning: $success", $perl_node] );
507 4813         8228 return ( $success, $perl_node );
508             }
509              
510             sub extract_file{###### All available potential nodes will be added if none are found only the first listed node will show as an empty node
511 37     35 1 5019 my ( $self, @node_list ) = ( @_ );
512             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
513             ###LogSD $self->get_all_space . '::XMLReader::extract_file', );
514             ###LogSD $phone->talk( level => 'debug', message =>[
515             ###LogSD 'Arrived at extract_file for the nodes:', @node_list ] );
516             ###LogSD $self->_print_current_file( $self->get_file );
517              
518             # Provide a dump-the-whole-thing out
519 37         56 my $fh;
520 47 100       182 if( $node_list[0] eq 'ALL_FILE' ){
521             ###LogSD $phone->talk( level => 'debug', message =>[
522             ###LogSD "Returning the whole file handle" ] );
523 16 50       22560 if( $self->has_file ){
524 4 50       108 open( $fh, "<&", $self->get_file ) or confess "Couldn't dup the file handle for 'ALL_FILE': $!";
525             ###LogSD $phone->talk( level => 'debug', message =>[
526             ###LogSD "First line of file is: ", $fh->getline ] );
527 4         18 $fh->seek( 0, 0 );
528 4         25 return $fh;
529             }else{
530             ###LogSD $phone->talk( level => 'debug', message =>[
531             ###LogSD "Looking for a file handle that doesn't exist" ] );
532             }
533             }
534              
535             # Get the header
536 31         921 my $file_string = $self->get_header;
537             ###LogSD $phone->talk( level => 'debug', message =>[
538             ###LogSD "Header string: " . ($file_string//'undef') ] );
539              
540             # Build a temp file and load it with the file string
541 31         6507 $fh = IO::File->new_tmpfile;
542 43         229 $fh->binmode();
543 35         545 print $fh "$file_string";########## No newlines since there are differences between windows
544             ###LogSD $self->_print_current_file( $fh );
545              
546             # Provide a nothing-file out
547 43 100       160 if( $node_list[0] eq 'NO_FILE' ){
548             ###LogSD $phone->talk( level => 'debug', message =>[
549             ###LogSD "Returning an empty(ish) handle" ] );
550 17         98 print $fh "<NO_FILE/>";
551 17         27333 return $fh;
552             }
553              
554             # Add nodes
555 28         41 my $found_a_node = 0;
556 28         43 my $first_node;
557 28         63 for my $node_name ( @node_list ){
558 28 100       120 my @parse_commands = is_ArrayRef( $node_name ) ? @$node_name : ( $node_name );
559             ###LogSD $phone->talk( level => 'debug', message =>[
560             ###LogSD "Advancing to node -$parse_commands[0]- incrementally: " . ($parse_commands[1]//1), $self->current_named_node ] );
561             ###LogSD $self->_print_current_file( $fh );
562 28         259 $self->start_the_file_over;
563             ###LogSD $phone->talk( level => 'debug', message =>[
564             ###LogSD "File reset to the beginning" ] );
565 28 50       95 $first_node = $parse_commands[0] if !defined $first_node;
566 28 100 66     153 my $name_match = ($parse_commands[1] and !is_Int( $parse_commands[1] )) ? pop @parse_commands : undef;
567 28         114 my $result = 0;
568 28         75 while( !$result ){
569 42         160 ($result, my $current_node_name, my $current_node_level) = $self->advance_element_position( @parse_commands ); ##### Split out the second element here and test for name
570             ###LogSD $phone->talk( level => 'debug', message =>[
571             ###LogSD "Advance result: " . ($result//'fail')] );
572 42 100       134 last if !$result;
573 34 100       97 if( !$name_match ){
574             ###LogSD $phone->talk( level => 'debug', message =>[
575             ###LogSD "No name matching required"] );
576 9         14 last;
577             }else{
578 25         109 my $current_node = $self->current_node_parsed;
579 25         43 my @name_key_list = grep( /name/i, keys %{$current_node->{$parse_commands[0]}} );
  25         172  
580             ###LogSD $phone->talk( level => 'debug', message =>[
581             ###LogSD "Looking in current node:", $current_node,
582             ###LogSD "..for a name match to -$name_match- using top key -$parse_commands[0]- and name key: $name_key_list[0]" ] );
583 25 100       91 if( $current_node->{$parse_commands[0]}->{$name_key_list[0]} eq $name_match ){
584             ###LogSD $phone->talk( level => 'debug', message =>[
585             ###LogSD "Found the node -$parse_commands[0]- named: $name_match"] );
586 11         34 last;
587             }else{
588             ###LogSD $phone->talk( level => 'debug', message =>[
589             ###LogSD "No node name match for -$name_match- with: $current_node->{$parse_commands[0]}->{$name_key_list[0]}", ] );
590 14         57 $result = 0;
591             }
592             }
593             }
594 28 100       104 if( $result ){
595 20         96 my $node_string = $self->_get_node_all;
596             ###LogSD $phone->talk( level => 'debug', message =>[
597             ###LogSD "Node string:", $node_string ] );
598 20         109 print $fh $node_string;
599             ###LogSD $self->_print_current_file( $fh );
600 20         86 $found_a_node = 1;
601             }
602             }
603              
604             # Add the first node as an empty node if none found
605 28 100       87 if( !$found_a_node ){
606             ###LogSD $phone->talk( level => 'debug', message =>[
607             ###LogSD 'None of the requested nodes were found', ] );
608 8         43 print $fh "<$first_node/>";# Returns a dummy file - file content should be tested by file type
609             ###LogSD $self->_print_current_file( $fh );
610             }
611              
612             ###LogSD $phone->talk( level => 'debug', message =>[
613             ###LogSD 'Final file handle:', $fh ] );
614             ###LogSD $self->_print_current_file( $fh );
615 28         177 $fh->seek( 0, 0 ); # rewind the file for processing
616 28         2343 return $fh;
617             }
618              
619             sub current_node_parsed{
620 468     468 1 702 my ( $self,) = ( @_ );
621             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
622             ###LogSD $self->get_all_space . '::XMLReader::current_node_parsed', );
623             ###LogSD $phone->talk( level => 'debug', message =>[
624             ###LogSD 'Arrived at current_node_parsed', ] );
625 468         596 my $node_ref;
626 468         14834 $node_ref->[0] = clone( $self->_current_node );
627             ###LogSD $phone->talk( level => 'debug', message => [
628             ###LogSD "The current node ref is:", $node_ref ] );
629              
630             # Handle empty node
631 468 50       5673 if( !$node_ref->[0] ){
632             ###LogSD $phone->talk( level => 'debug', message => [
633             ###LogSD "Reached the end of the file" ] );
634 2         106 return undef;
635             }
636              
637             # Walk back a raw_text node
638 468 100 66     2371 if( $node_ref->[0] and $node_ref->[0]->{name} eq 'raw_text' ){
639             ###LogSD $phone->talk( level => 'debug', message => [
640             ###LogSD "The last node is a text node" ] );
641 83         6441 push @$node_ref, clone( $self->_prior_node );
642             ###LogSD $phone->talk( level => 'debug', message => [
643             ###LogSD "...now the current node ref is:", $node_ref ] );
644             }
645              
646             # Build out the return
647             ###LogSD $phone->talk( level => 'trace', message =>[
648             ###LogSD "Building out the node:", $node_ref, ] );
649 466         1189 $self->_build_out_the_return( $node_ref, );
650              
651             # pull the compiled ref for return
652 466         12273 my $built_reference = $self->_remove_ref;
653             ###LogSD $phone->talk( level => 'trace', message =>[
654             ###LogSD "Final result:", $built_reference, ] );
655 468         11396 $self->_set_ref_stack( [] );
656 466         11883 $self->_set_position_stack( [] );
657 466         1213 $built_reference = $self->squash_node( $built_reference );
658             ###LogSD $phone->talk( level => 'trace', message =>[
659             ###LogSD "Squashed node:", $built_reference, ] );
660              
661 466         1599 return $built_reference;
662             }
663              
664             sub close_the_file{
665 295     293 1 404 my ( $self ) = @_;
666             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
667             ###LogSD 'Spreadsheet::Reader::ExcelXML::XMLReader::DEMOLISH::close_the_file', );# $self->get_all_space .
668             ###LogSD $phone->talk( level => 'debug', message => [
669             ###LogSD "clearing the XMLReader reader for log space:",
670             ###LogSD 'Spreadsheet::Reader::ExcelXML::XMLReader::DEMOLISH::close_the_file', ] );# $self->get_all_space .
671              
672             # Close the file
673 295 100       11437 if( $self->has_file ){
674             ###LogSD $phone->talk( level => 'debug', message =>[ "Closing the file handle", ] );
675 161         2537 $self->close;
676 161         1897833 $self->clear_file;
677             }
678             #~ print "XMLReader file check complete\n";
679             }
680              
681             sub initial_node_build{
682 12662     12659 1 18091 my( $self, $name, $array_list_ref ) = @_;
683             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
684             ###LogSD $self->get_all_space . '::XMLReader::_hidden::initial_node_build', );
685             ###LogSD $phone->talk( level => 'debug', message => [
686             ###LogSD "attempting to build the node named -$name- for string list:", $array_list_ref ] );
687              
688 12662         21557 my $node_ref->{name} = $name;
689 12662         36417 $node_ref->{type} = 'regular';
690              
691             # Set node level - Potentially this could be a separate 'partial stack' that could report to 'should_be_stacking'?
692             $node_ref->{level} = !$self->not_end_of_file ? 0 :
693 12661 100       380431 ($self->_current_node->{level} + ($self->_current_node->{closed} eq 'closed' ? 0 : 1));
    100          
694             ###LogSD $phone->talk( level => 'debug', message =>[ "updated node ref:", $node_ref ] );
695              
696             # Set node to open (default fixed elswhere)
697 12661         19066 $node_ref->{closed} = 'open';
698             ###LogSD $phone->talk( level => 'debug', message =>[ "updated node ref:", $node_ref ] );
699              
700             # Store remaining elements
701 12659 100       22506 $node_ref->{attribute_strings} = $array_list_ref if scalar @$array_list_ref;
702             ###LogSD $phone->talk( level => 'debug', message =>[ "updated node ref:", $node_ref ] );
703              
704 12659         12956 return $node_ref;
705             }
706             #########1 Private Attributes 3#########4#########5#########6#########7#########8#########9
707              
708             has _loaded =>(
709             isa => Bool,
710             writer => 'good_load',
711             reader => 'loaded_correctly',
712             default => 0,
713             );
714              
715             has _node_stack =>(
716             isa => ArrayRef,
717             traits => ['Array'],
718             reader => '_get_node_stack',
719             writer => '_set_node_stack',
720             default => sub{ [] },
721             handles =>{
722             add_node_to_stack => 'push',
723             not_end_of_file => 'count',
724             _remove_node => 'pop',
725             _remove_header => 'shift',
726             _current_node =>[ get => -1 ],
727             _prior_node =>[ get => -2 ],
728             #~ _get_node_position => 'get',
729             }
730             );
731              
732             has _ref_stack =>(
733             isa => ArrayRef,
734             traits => ['Array'],
735             reader => '_get_ref_stack',
736             writer => '_set_ref_stack',
737             default => sub{ [] },
738             handles =>{
739             _add_ref => 'push',
740             _remove_ref => 'pop',
741             _has_refs => 'count',
742             }
743             );
744              
745             has _position_stack =>(
746             isa => ArrayRef,
747             traits => ['Array'],
748             reader => '_get_position_stack',
749             writer => '_set_position_stack',
750             default => sub{ [] },
751             handles =>{
752             _add_position => 'push',
753             _remove_position => 'pop',
754             _has_positions => 'count',
755             _last_position =>[ get => -1 ],
756             }
757             );
758              
759             has _string_stack =>(
760             isa => ArrayRef,
761             traits => ['Array'],
762             reader => '_get_string_stack',
763             writer => '_set_string_stack',
764             default => sub{ [] },
765             handles =>{
766             _add_string => 'push',
767             _remove_string => 'pop',
768             _has_strings => 'count',
769             }
770             );
771              
772             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
773              
774             sub _start_xml_reader{
775 214     191   49110 my( $self, $file_handle ) = @_;
776             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
777             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_start_xml_reader', );
778             ###LogSD $phone->talk( level => 'debug', message => [
779             ###LogSD "turning a file handle into an xml reader", ] );
780              
781             # Clear any old settings
782 214         6512 $self->_clear_xml_version;
783 214         48216 $self->_clear_xml_encoding;
784 214         9994 $self->_clear_xml_progid;
785 214         47893 $self->_clear_xml_header;
786 191         5088 $self->clear_location;
787 191         5277 $self->_set_node_stack( [] );
788 191         5255 $self->_set_ref_stack( [] );
789 191         5355 $self->_set_position_stack( [] );
790 191         5239 $self->_set_string_stack( [] );
791              
792             # (re) set the file to 0 for insurance
793             ###LogSD $phone->talk( level => 'debug', message =>[ "start at the beginning" ] );
794 191         1206 $file_handle->seek( 0, 0 );
795             ###LogSD $phone->talk( level => 'trace', message => [
796             ###LogSD "ran seek( 0, 0 ) -> (to the beginning of the file)", ] );
797              
798             # Kick start the file read
799 191         46547 $self->_read_file;
800              
801             # Set the file unique bits
802             ###LogSD $phone->talk( level => 'debug', message =>[
803             ###LogSD "Check if this type of file has unique settings" ], );
804 191 100       944 if( $self->can( 'load_unique_bits' ) ){
805             ###LogSD $phone->talk( level => 'debug', message =>[ "Loading unique bits" ], );
806 178         3980 $self->load_unique_bits;
807             ###LogSD $phone->talk( level => 'debug', message =>[
808             ###LogSD "Finished loading unique bits" ], );
809             }
810              
811             ###LogSD $phone->talk( level => 'debug', message => [ "finished all xml reader build steps" ], );
812 191         4554 return 1;
813             }
814              
815             sub _next_element{
816 1247     1245   5324 my( $self, $element ) = @_;
817             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
818             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_next_element', );#::_hidden
819             ###LogSD $phone->talk( level => 'debug', message => [
820             ###LogSD "searching for the next element: $element", ] );
821 1247         2289 my( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;# Implied in next since the last one may also be $element
822 1247   100     15099 NODEINDEX: while( $result_type == 1 or (($result_type != 0) and ($top_node_name ne $element)) ){
      66        
823 6282         18821 ( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
824             ###LogSD $phone->talk( level => 'debug', message => [
825             ###LogSD "result of the read file action: $result_type", $top_node_name, $top_node_level, $result, ] );
826 6282   66     39288 while( defined $result_type and $result_type == 1 ){
827             ###LogSD $phone->talk( level => 'debug', message => [
828             ###LogSD "The last node was only a closing tag - index once again to get a new tag" ] );
829 3583         5275 ( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
830             ###LogSD $phone->talk( level => 'debug', message => [
831             ###LogSD "result of the read file action: $result_type", $result, ] );
832 3583 100       31554 last NODEINDEX if $result_type == 0;
833             }
834             }
835 1351         197733 return ( $result_type, $top_node_name, $top_node_level, $result );
836             }
837              
838             sub _next_unnamed_element{
839 10     10   16 my( $self ) = @_;
840             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
841             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_next_unnamed_element', );
842             ###LogSD $phone->talk( level => 'debug', message => [
843             ###LogSD "searching for the next unnamed element", ] );
844 10         22 my( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;# Implied in next since the last one may also be $element
845             ###LogSD $phone->talk( level => 'debug', message => [
846             ###LogSD "result of the read file action: $result_type", $result, $self->_get_node_stack ] );
847 10         30 NODEINDEX: while( $result_type == 1 ){
848             ###LogSD $phone->talk( level => 'debug', message => [
849             ###LogSD "The last node was only a closing tag - index once again to get a new tag" ] );
850 0         0 ( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
851             ###LogSD $phone->talk( level => 'debug', message => [
852             ###LogSD "result of the read file action: $result_type", $result, $self->_get_node_stack ] );
853 6 0       189 last NODEINDEX if $result_type == 0;
854             }
855 10         24 return( $result_type, $top_node_name, $top_node_level, $result );#Start with Bang Bang operator
856             }
857              
858             sub _build_out_the_return{
859 5403     5403   4911 my( $self, $add_list, ) = @_;# $target_level
860             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
861             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_build_out_the_return', );
862             ###LogSD $phone->talk( level => 'trace', message =>[
863             ###LogSD "Against stored node list:", $self->_get_ref_stack,
864             ###LogSD "Building out the return:", $add_list,
865             ###LogSD "...and stored position list:", $self->_get_position_stack,] );
866             #~ ###LogSD (defined $target_level ? "stopping at level: $target_level" : undef)
867              
868 5403 100 66     18785 if( !$add_list or scalar( @$add_list ) == 0 ){
869             ###LogSD $phone->talk( level => 'debug', message =>[
870             ###LogSD "No new elements passed for addition" ] );
871 23         15730 return 0;
872             }
873              
874             # Build the reference
875 5393         39534 my( $top_reference, $base_reference, $last_level );
876              
877             # Handle stacking new on top of old
878 5380 50       8373 exit 1 if !exists $add_list->[0]->{level};
879             ###LogSD $phone->talk( level => 'debug', message =>[
880             ###LogSD "Checking if there are positions to add: ". ($self->_has_positions//'undef'),
881             ###LogSD ( $self->_has_positions ? "Comparing level -$add_list->[0]->{level}- against stored level: " . $self->_last_position : undef) ] );
882 5380 100 100     151743 if( $self->_has_positions and $add_list->[0]->{level} == $self->_last_position - 1 ){
883 1505         38952 $top_reference = $self->_remove_ref;
884 1505         40635 $self->_remove_position;
885             ###LogSD $phone->talk( level => 'debug', message =>[
886             ###LogSD "New top reference:", $top_reference ] );
887             }
888              
889 5393         7596 for my $element ( @$add_list ){
890              
891             # store the node level
892 7287         30562 $last_level = $element->{level};
893              
894             # Parse the attributes if they exist
895 7274 100       10244 if( exists $element->{attribute_strings} ){
896             ###LogSD $phone->talk( level => 'debug', message =>[
897             ###LogSD "Processing raw attribute list:", $element->{attribute_strings} ] );
898 3701         7153 my @attribute_args = $self->_reconcile_attribute_strings( $element->{attribute_strings} );
899             ###LogSD $phone->talk( level => 'debug', message =>[
900             ###LogSD "Reconciled attribute list:", @attribute_args ] );
901 3701         6481 $element->{attribute_strings} = [ @attribute_args ];
902 3714 100 100     16624 $element = (defined $element->{name} and $element->{name} eq 'DOCTYPE') ? $self->_build_doctype_attributes( $element ) : $self->_build_regular_attributes( $element ) ;
903 3718         21048 delete $element->{attribute_strings};
904             ###LogSD $phone->talk( level => 'debug', message =>[
905             ###LogSD "Updated element:", $element ] );
906             }
907              
908             ###LogSD $phone->talk( level => 'debug', message =>[
909             ###LogSD "processing element:", $element, "..at level: " . ($last_level//'undef'), $top_reference ] );
910 7291         6175 my $stop_level = 'debug';
911 7289 100 100     23254 if( !exists $element->{name} ){
    100          
912             ###LogSD $phone->talk( level => 'debug', message =>[ "Handling unnamed element" ] );
913 993         32041 delete $element->{level};
914 993 100       1854 if( exists $element->{val} ){
915 19         26289 $top_reference = $element->{val};
916             }else{
917 989         2345 for my $key ( keys %$element ){
918 503         650 push @{$top_reference->{list_keys}}, $key;
  503         1031  
919 505         35158 push @{$top_reference->{list}}, $element->{$key};
  505         44539  
920             }
921             }
922             }elsif( exists $element->{name} and $element->{name} eq 'raw_text' ){
923 1355 50       2252 confess "I already have a top reference but I'm trying to add a text node" if $top_reference;
924 1342         2308 $top_reference = { raw_text => $self->_remove_escapes( $element->{raw_text} ) };
925             }else{
926              
927             # Split out element values to allow for sub-reffing
928 4962         11758 my $name = $element->{name};
929 4962         4077 my $level = $element->{level};
930 4958         5402 map{ delete $element->{$_} } qw( name closed type level initial_string );
  24794         25519  
931             ###LogSD $phone->talk( level => 'debug', message =>[
932             ###LogSD "processing element named -$name- at level -$level- with content:", $element, $top_reference ] );
933 4960 100 100     12514 if( exists $element->{attributes} and is_HashRef( $top_reference ) ){
934 1150 100       5920 if( exists $top_reference->{list} ){
    50          
935             ###LogSD $phone->talk( level => 'debug', message =>[
936             ###LogSD "Adding top ref:", $top_reference, "to element named -$name- at level -$level- with content:", $element, ] );
937 858         1072 $element->{list} = $top_reference->{list};
938 858         953 $element->{list_keys} = $top_reference->{list_keys};
939 856         949 $top_reference = undef;
940             ###LogSD $phone->talk( level => 'debug', message =>[
941             ###LogSD "Updated element:", $element, ] );
942             }elsif( exists $top_reference->{raw_text} ){
943             ###LogSD $phone->talk( level => 'debug', message =>[
944             ###LogSD "Adding raw_text ref:", $top_reference, "to element named -$name- at level -$level- with content:", $element, ] );
945 292         254 push @{$element->{list}}, $top_reference->{raw_text};
  305         674  
946 305         316 push @{$element->{list_keys}}, 'raw_text';
  305         520  
947 357         431 $top_reference = undef;
948             ###LogSD $phone->talk( level => 'debug', message =>[
949             ###LogSD "Updated element:", $element, ] );
950             }
951             }
952 4971 100       19495 $element = undef if !scalar( %$element );
953 4971 100       35607 $base_reference =
954             $top_reference ? $top_reference : $element;
955 4958         3894 $top_reference = undef;# poor mans clone
956              
957             # Build in any stored information at this level
958 4958 100 100     146744 if( $self->_has_positions and $level == $self->_last_position ){
959 2241         58982 my $stored_ref = $self->_remove_ref;
960 2241         60966 $self->_remove_position;
961             ###LogSD $phone->talk( level => 'debug', message =>[
962             ###LogSD "Adding stored ref:", $stored_ref, "to element named -$name- at level -$level- with content:", $element, $top_reference ] );
963             #~ $stop_level = 'fatal';
964 2241 100       3479 if( exists $stored_ref->{list} ){
965 2241         1678 @{$top_reference->{list_keys}} = @{$stored_ref->{list_keys}};
  2241         7228  
  2241         2322  
966 2241         1959 @{$top_reference->{list}} = @{$stored_ref->{list}};
  2241         6288  
  2241         2082  
967             }
968             }
969              
970             # Load the current element
971 4958         4910 push @{$top_reference->{list_keys}}, $name;
  4958         9011  
972 4971         3888 push @{$top_reference->{list}}, $base_reference;
  4971         8847  
973             ###LogSD $phone->talk( level => 'debug', message =>[ "Top ref:", $top_reference ] );
974             }
975             ###LogSD $phone->talk( level => $stop_level, message =>[
976             ###LogSD "processing result:", $top_reference, $base_reference ] );
977             }
978 5393         143176 $self->_add_ref( $top_reference );
979 5393         143304 $self->_add_position( $last_level );
980             ###LogSD $phone->talk( level => 'debug', message =>[
981             ###LogSD "Current ref stack:", $self->_get_ref_stack, $self->_get_position_stack ] );
982              
983 5385         12579 return 1;
984             }
985              
986             sub _remove_escapes{
987 17567     17562   14182 my( $self, $string) = @_;
988              
989             # Return 0 length
990 17567 100 100     39509 if( !defined $string or length( $string ) == 0 ){
991 8096         24207 return $string;
992             }
993             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
994             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_remove_escapes', );
995             ###LogSD $phone->talk( level => 'debug', message => [
996             ###LogSD "removing escapes from the string: $string" ] );
997              
998             # Handle xml escapes
999 9476         9005 $string =~ s/&lt;/</g;
1000 9476         48877 $string =~ s/&gt;/>/g;
1001 9476         72525 $string =~ s/&quot;/"/g;
1002 9476         6891 $string =~ s/&amp;/&/g;
1003 9476         6684 $string =~ s/&apos;/'/g;
1004 9476         6341 $string =~ s/&#60;/</g;
1005 9484         6427 $string =~ s/&#62;/>/g;
1006 9484         6312 $string =~ s/&#34;/"/g;
1007 9484         6397 $string =~ s/&#38;/&/g;
1008 9484         6183 $string =~ s/&#39;/'/g;
1009             ###LogSD $phone->talk( level => 'debug', message =>[ "updated string: $string"] );
1010              
1011 9484         12703 return $string;
1012             }
1013              
1014             sub _read_file{
1015 24059     24003   53108 my( $self, ) = @_;
1016             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1017             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_read_file', );
1018             ###LogSD $phone->talk( level => 'debug', message => [
1019             ###LogSD "reading the 'line' from the file", ] );# caller( 0 ), caller( 1 ), caller( 2 )
1020              
1021             # get the next file line - and scrub it
1022 24055         52286 my $node_type = 0; #( 0 = EOF, 1 = closing only tag, 2 = open tag, 3 = self contained (open and closed in one tag))
1023 24055         332138 my @sections;
1024 24016         408259 while( !@sections ){
1025 24349         82476 my $line = $self->getline;
1026             ###LogSD $phone->talk( level => 'debug', message =>[ "The next file line is:", $line ] );
1027 24336 100       812598 if( !$line ){
1028             ###LogSD $phone->talk( level => 'debug', message =>[ "Reached the end of the file" ] );
1029 362         2065 return( 0, 'EOF', 0, [ { name => 'EOF', level => -1 } ] );
1030             }
1031 24219         48509 $line = substr( $line, 0, -1 );
1032 24219         422545 @sections = split />/, $line;
1033             ###LogSD $phone->talk( level => 'debug', message =>[ "Line sections are:", @sections ] );
1034 23978 100       59064 if( scalar( @sections ) > 2 ){
1035 1         19 $self->set_error( 'The next xml line broke into more than 2 sections after |' . $sections[0] . '| from line: ' . $line );
1036 242         811 $self->close_the_file;
1037 242         988 $self->good_load( 0 );
1038 242         1120 return( 0, 'BAD' );
1039             }
1040             }
1041              
1042             # Pull name, type, and attributes as well as calculating depth
1043 23885         414401 my @closures = qw( open closed );
1044 23644         17141 my $is_xml_header = 0;
1045 23644         15838 my $x = 0;
1046 23644         20817 my $return = [];
1047 23644         17442 my( $top_node_name, $top_node_level );
1048 23881         25401 for my $node ( @sections ){
1049             ###LogSD $phone->talk( level => 'debug', message =>[ "Processing section: ", $node] );
1050 28339         19268 my( $node_ref, $node_name, @node_split );
1051 28339         23369 my $initial_string = $node;
1052              
1053             # Handle the first pass
1054 28339 100       34743 if( $x == 0 ){
1055              
1056             # Handle header nodes with quotes
1057 23881 100 100     83715 if( substr( $node, 0, 1 ) eq '?' or substr( $node, 0, 1 ) eq '!' ){
1058 626         887 $is_xml_header = 1;
1059 630 100       2179 $node = (substr( $node, 0, 1 ) eq '?') ? substr( $node, 1, -1 ) : substr( $node, 1 ) ;
1060             ###LogSD $phone->talk( level => 'debug', message =>[
1061             ###LogSD "Removed question marks from node: " . $node,
1062             ###LogSD "is_xml_header set to: $is_xml_header" ] );
1063             }
1064              
1065             # Handle end nodes - always subtractive to the stack and then exits
1066 23885 100       413282 if( substr( $node, 0, 1 ) eq '/' ){
1067             ###LogSD $phone->talk( level => 'debug', message =>[ "Reached an end node" ] );
1068              
1069             # For stacking off, ignore end nodes
1070 9851 100       239160 if( !$self->should_be_stacking ){
1071             ###LogSD $phone->talk( level => 'debug', message =>[ "No stacking required - ignoring end node" ] );
1072 1533         6504 return( $self->_read_file );
1073             }
1074              
1075             # Handle previously closed nodes
1076 8555         216653 while( $self->_current_node->{closed} eq 'closed' ){
1077 4377         115367 push @$return, $self->_remove_node;
1078             ###LogSD $phone->talk( level => 'debug', message =>[
1079             ###LogSD "pushed the closed end node to return - looking for an open node - current type: $node_type", $return ] );
1080             }
1081              
1082             # Process the first open node
1083 8322         212285 my $current_node = $self->_remove_node;
1084             ###LogSD $phone->talk( level => 'debug', message =>[
1085             ###LogSD "Current node:", $current_node ] );
1086              
1087             # Lookup the name
1088 8322         9614 $node_name = substr( $node, 1 );
1089 8555 50       20790 if( $current_node->{name} ne $node_name ){
1090 104         514 confess "Found an end node -$node_name- that doesn't match the next open node:" . Dumper( $current_node );
1091             }
1092 8422         179515 $current_node->{closed} = 'closed';
1093 8418         8857 push @$return, $current_node;
1094             ###LogSD $phone->talk( level => 'debug', message =>[
1095             ###LogSD "Return list type -1- with current nodes:", $return ] );
1096              
1097             # Return and let the caller determine if it wants to proceed
1098 8418         180793 return( 1, $node_name, $return->[-1]->{level}, $return );# Always $node_type = 1
1099             }
1100              
1101             # handle self closing nodes
1102 14038         10116 my $self_closing;
1103 14038 100       20268 if( substr( $node, -1, 1 ) eq '/' ){
1104 3818         4332 $node = substr( $node, 0, -1 );
1105 3818         3236 $self_closing = 1;
1106             ###LogSD $phone->talk( level => 'debug', message =>[
1107             ###LogSD "Found a self closing node" ] );
1108             }
1109             # Pull the node name
1110 14038         34862 @node_split = split /\s/, $node;
1111             ###LogSD $phone->talk( level => 'debug', message =>[
1112             ###LogSD "node split is:", @node_split ] );
1113 14038         23829 $node_name = shift @node_split;
1114 14038 50       22209 $top_node_name = $node_name if !$top_node_name;
1115              
1116             # Exit for speed return when !should_be_stacking
1117 14034 100       355166 if( !$self->should_be_stacking ){
1118             ###LogSD $phone->talk( level => 'debug', message =>[
1119             ###LogSD "Stacking is off - returning found node name: $node_name" ] );
1120 1431         5127 return( 2, $node_name, undef, [ @node_split ] );# Is level worth calculating here? (no node stack popping done (yet?) either)
1121             }
1122              
1123             # Check for white space in the xml file
1124 12611 100 100     333005 if( $self->not_end_of_file and
1125             $self->_current_node->{name} eq 'raw_text' ){
1126             ###LogSD $phone->talk( level => 'debug', message =>[
1127             ###LogSD "found previously stored white space", $self->_get_node_stack ] );
1128 1196         31063 $self->_remove_node;
1129             ###LogSD $phone->talk( level => 'trace', message =>[
1130             ###LogSD "White space gone", $self->_get_node_stack ] );
1131             }
1132              
1133             # Build the node
1134 12611         39343 $node_ref = $self->initial_node_build( $node_name, [@node_split] );
1135 12740 100 100     38736 if( $is_xml_header or $self_closing ){
1136 4211         4535 $node_ref->{closed} = 'closed';
1137             }
1138             ###LogSD $phone->talk( level => 'debug', message =>[
1139             ###LogSD "Returned from initial node build with node:", $node_ref ] );
1140 12628         10913 $top_node_level = $node_ref->{level};
1141 12628         9146 $node_type = 2;
1142              
1143             # pop nodes at the same or lower level
1144 12628   100     341043 while($self->not_end_of_file and $self->_current_node->{level} >= $node_ref->{level} ){
1145 3025         108244 push @$return, $self->_remove_node;
1146             ###LogSD $phone->talk( level => 'trace', message =>[
1147             ###LogSD "Return ref now", $return, ] );
1148             }
1149              
1150             }else{
1151 4591         4812 $node_name = 'raw_text';
1152 4591         222008 @$node_ref{qw( name raw_text type closed )} = ( 'raw_text', $node, '#text', 'closed' );
1153 4591         5450 $node_ref->{level} = $top_node_level + 1;
1154             ###LogSD $phone->talk( level => 'trace', message =>[
1155             ###LogSD "Raw text node:", $node_ref, ] );
1156             }
1157              
1158             # Store the node
1159 17198         25036 $node_ref->{initial_string} = $initial_string;
1160 17179         459894 $self->add_node_to_stack( $node_ref );
1161             ###LogSD $phone->talk( level => 'trace', message =>[
1162             ###LogSD "Updated node stack", $self->_get_node_stack, "node_type at: $node_type" ] );
1163 17179         188124 $x++;
1164 17084 100       38214 last if $node_ref->{closed} eq 'closed';
1165             }
1166              
1167             # Handle header nodes then move on
1168 12609 100       16881 if( $is_xml_header ){
1169             ###LogSD $phone->talk( level => 'trace', message =>[
1170             ###LogSD "Found an xml header", ($return ? ("..with initial return:", $return) : undef) ] );
1171 391         4754 ( $node_type, $top_node_name, $top_node_level, my $sub_return ) = $self->_read_file;
1172             ###LogSD $phone->talk( level => 'trace', message =>[
1173             ###LogSD "Read result after header: $node_type", $sub_return ] );
1174 391 100 66     10592 $self->_load_header( $sub_return ) if !$self->_has_xml_header and @$sub_return;
1175             }
1176              
1177             # If you made it here the process worked
1178             ###LogSD $phone->talk( level => 'trace', message =>[
1179             ###LogSD "Updated node stack", $self->_get_node_stack,
1180             ###LogSD "returning popped nodes:", $return, "node_type at: $node_type"] );
1181 12626         32520 return( $node_type, $top_node_name, $top_node_level, $return );# Always $node_type = 2
1182             }
1183              
1184             sub _load_header{
1185 224     205   663 my( $self, $header_nodes) = @_;
1186             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1187             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_load_header', );
1188             ###LogSD $phone->talk( level => 'trace', message =>[
1189             ###LogSD "Loading headers:", $header_nodes ] );
1190              
1191             # Check for top level header string
1192 220 100       695 if( $header_nodes->[0]->{name} eq 'xml' ){
1193 204         2596 my $header_string = '<' . $header_nodes->[0]->{initial_string} . '>';
1194             ###LogSD $phone->talk( level => 'trace', message =>[
1195             ###LogSD "Setting the primary header string: $header_string", ] );
1196 204         45051 $self->_set_xml_header( $header_string );
1197             }
1198              
1199             # Transform the data
1200 224         906 $self->_build_out_the_return( $header_nodes );
1201 224         6715 my $built_reference = $self->_remove_ref;
1202             ###LogSD $phone->talk( level => 'trace', message =>[
1203             ###LogSD "Final result result:", $built_reference ] );
1204 214         8259 $self->_set_ref_stack( [] );
1205 214         5159 $self->_set_position_stack( [] );
1206 209         8071 my $header_node = $self->squash_node( clone( $built_reference ) );
1207              
1208             ###LogSD $phone->talk( level => 'debug', message => [
1209             ###LogSD "loading file level settings since the header was found", $header_node] );
1210             my $test_ref =
1211             exists $header_node->{xml} ? $header_node->{xml} :
1212             exists $header_node->{'mso-application'} ? $header_node->{'mso-application'} :
1213 209 50       674 exists $header_node->{'DOCTYPE'} ? $header_node : {};
    100          
    100          
1214              
1215 209         4121 for my $attribute ( qw( version encoding progid DOCTYPE ) ){
1216 818 100       2148 if( exists $test_ref->{$attribute} ){
1217             #~ if( $attribute eq 'encoding' ){
1218             #~ $test_ref->{$attribute} = $test_ref->{$attribute} eq 'UTF-8' ? 'utf8' : $test_ref->{$attribute};
1219             #~ my $encoding = ":encoding($test_ref->{$attribute})";
1220             #~ ###LogSD $phone->talk( level => 'debug', message => [
1221             #~ ###LogSD "Setting file handle encoding to -> $encoding", ] );
1222             #~ print "Setting file handle encoding to -> $encoding\n";
1223             #~ $self->binmode( $encoding );
1224             #~ }
1225 374         7617 my $setter = '_set_xml_' . lc( $attribute );
1226             ###LogSD $phone->talk( level => 'debug', message => [
1227             ###LogSD "Performing the action -$setter- on the data: $test_ref->{$attribute}", ] );
1228 374         11789 $self->$setter( $test_ref->{$attribute} );
1229             }
1230             }
1231             }
1232              
1233             sub _get_node_all{
1234 45     20   4922 my ( $self, $level, ) = @_;# $attribute_ref
1235             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1236             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_get_node_all', );
1237             ###LogSD $phone->talk( level => 'debug', message =>[
1238             ###LogSD "Parsing current element", (defined $level ? "..to depth: $level" : undef), ] );###LogSD (defined $attribute_ref ? "..with attribute_ref:" : undef), $attribute_ref
1239              
1240             # Check for end of file state
1241 45 50       54651 if( !$self->not_end_of_file ){
1242             ###LogSD $phone->talk( level => 'debug', message =>[ "Reached end of file" ] );
1243 31         204 return 'EOF';
1244             }
1245              
1246             # Check for self contained node
1247 43         163 my $current_node = clone( $self->current_named_node );
1248             ###LogSD $phone->talk( level => 'debug', message =>[ "Current node is:", $current_node ] );
1249 28 50       132 if( $current_node->{closed} eq 'closed' ){
1250             ###LogSD $phone->talk( level => 'debug', message =>[ "Found a self contained node: ", $current_node ] );
1251 8         12825 map{ delete $current_node->{$_} } qw( initial_string );# level
  4         70  
1252 4         7242 $self->_build_out_the_string( [ $current_node ] );
1253              
1254             # pull the compiled ref for return
1255 19         802 my $built_reference = $self->_remove_string;
1256             ###LogSD $phone->talk( level => 'trace', message =>[
1257             ###LogSD "Final result result:", $built_reference ] );
1258 19         47379 $self->_set_string_stack( [] );
1259 0         0 $self->_set_position_stack( [] );
1260              
1261 0         0 return $built_reference;
1262             }
1263              
1264             # Build target name and level
1265 20         62 my( $target_node, $target_level ) = @$current_node{ qw( name level ) };
1266 20 50       64 $target_level = defined $level ? ($target_level + $level) : undef;
1267             ###LogSD $phone->talk( level => 'debug', message =>[
1268             ###LogSD "Target node is: $target_node",
1269             ###LogSD (defined $target_level ? "..and target level is: $target_level" : undef ) ] );
1270              
1271             # Cycle to the bottom and back up
1272 20         22 my $done;
1273 20         52 ADDSTRINGS: while( !$done ){
1274             ###LogSD $phone->talk( level => 'debug', message =>[
1275             ###LogSD "Looking for the next node in the file", ] );
1276 1929         2560 my( $result_type, $top_node_name, $top_node_level, $result ) = $self->_read_file;
1277             ###LogSD $phone->talk( level => 'debug', message =>[
1278             ###LogSD "Node read returned: $result_type", $result ] );
1279              
1280             # Handle any rewind and dump
1281 1929 100       4186 if( scalar( @$result ) > 0 ){
1282             ###LogSD $phone->talk( level => 'debug', message =>[
1283             ###LogSD "Reached the bottom of something",
1284             ###LogSD "..checking if: $target_node",
1285             ###LogSD "..equals: " . $result->[-1]->{name} ] );
1286              
1287             # Check if you reached the top
1288 1004 100       1514 if( $result->[-1]->{name} eq $target_node ){
1289             ###LogSD $phone->talk( level => 'debug', message =>[
1290             ###LogSD "received the very last return" ] );
1291 20         35 $done = 1;
1292             }
1293              
1294             # Exit if unexpectedly reached the end
1295 1004 50       1287 if( $result_type == 0 ){
1296             ###LogSD $phone->talk( level => 'trace', message =>[
1297             ###LogSD "Unexpected end of file:", $result ] );
1298 0         0 last ADDSTRINGS;
1299             }
1300              
1301             # Build out the return
1302             ###LogSD $phone->talk( level => 'trace', message =>[
1303             ###LogSD "Building out the result:", $result ] );
1304 1004         1309 $self->_build_out_the_string( $result, );
1305             }
1306             }
1307              
1308             # pull the compiled ref for return
1309 20         602 my $built_reference = $self->_remove_string;
1310             ###LogSD $phone->talk( level => 'trace', message =>[
1311             ###LogSD "Final result:", $built_reference ] );
1312 20         520 $self->_set_string_stack( [] );
1313 20         591 $self->_set_position_stack( [] );
1314              
1315 20         94 return $built_reference;
1316             }
1317              
1318             sub _build_out_the_string{
1319 1004     1004   784 my( $self, $add_list, ) = @_;
1320             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1321             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_build_out_the_string', );
1322             ###LogSD $phone->talk( level => 'trace', message =>[
1323             ###LogSD "Building out the node string:", $add_list,
1324             ###LogSD "Against stored string list:", $self->_get_string_stack,
1325             ###LogSD "...and stored position list:", $self->_get_position_stack, ] );
1326              
1327 1004 50 33     3308 if( !$add_list or scalar( @$add_list ) == 0 ){
1328             ###LogSD $phone->talk( level => 'debug', message =>[
1329             ###LogSD "No new elements passed for addition" ] );
1330 0         0 return 0;
1331             }
1332              
1333             # Build the reference
1334 1004         683 my( $top_string, $last_level );# $base_string,
1335              
1336             # Handle stacking new on top of old
1337 1004 100 100     27395 if( $self->_has_positions and $add_list->[0]->{level} == $self->_last_position - 1 ){
1338 449         11838 $top_string = $self->_remove_string;
1339 449         11766 $self->_remove_position;
1340             #~ $should_close_nodes = 1;
1341             ###LogSD $phone->talk( level => 'debug', message =>[
1342             ###LogSD "New top string:", $top_string ] );
1343             }
1344              
1345             # Turn the stack into a string
1346 1004         1205 for my $element ( @$add_list ){
1347             ###LogSD $phone->talk( level => 'debug', message =>[
1348             ###LogSD "Processing element:", $element ] );
1349              
1350             # store the node level
1351 1423         1121 $last_level = $element->{level};
1352 1423         1118 my $base_string = $top_string;
1353             ###LogSD $phone->talk( level => 'debug', message =>[
1354             ###LogSD "Updated last level -$last_level- and base_string: " . ($base_string//'undef') ] );
1355 1423 100       1896 if( $element->{type} eq '#text' ){
1356 359         355 $top_string = $element->{initial_string};
1357             }else{
1358 1064         1498 $top_string = '<' . $element->{initial_string} . '>';
1359             }
1360             ###LogSD $phone->talk( level => 'debug', message =>[
1361             ###LogSD "Updated top_string: " . $top_string ] );
1362 1423 100       2039 $top_string .= $base_string if $base_string;
1363             ###LogSD $phone->talk( level => 'debug', message =>[
1364             ###LogSD "Updated top_string: " . $top_string ] );
1365              
1366             # Close the open node
1367 1423 100 100     4101 if( $element->{type} ne '#text' and substr( $element->{initial_string}, -1 ) ne '/' ){
1368             ###LogSD $phone->talk( level => 'debug', message =>[
1369             ###LogSD "Closing the node: $element->{name}" ] );
1370 885         1022 $top_string .= '</' . $element->{name} . '>';
1371             }
1372             ###LogSD $phone->talk( level => 'debug', message =>[
1373             ###LogSD "New top_string:", $top_string ] );
1374              
1375             # Build in any stored information at this level
1376 1423 100 100     38361 if( $self->_has_positions and $last_level == $self->_last_position ){
1377 535         14365 my $stored_string = $self->_remove_string;
1378 535         14135 $self->_remove_position;
1379 535         1211 $top_string = $stored_string . $top_string;
1380             ###LogSD $phone->talk( level => 'debug', message =>[
1381             ###LogSD "Updated to string:", $top_string ] );
1382             }
1383             }
1384 1004         25750 $self->_add_string( $top_string);
1385 1004         25712 $self->_add_position( $last_level );
1386             ###LogSD $phone->talk( level => 'debug', message =>[
1387             ###LogSD "Current string stack:", $self->_get_string_stack, $self->_get_position_stack ] );
1388              
1389 1004         3328 return 1;
1390             }
1391              
1392             around getline => sub{
1393             local $/ = '<';
1394             my( $orig, $self, ) = @_;
1395             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1396             ###LogSD $self->get_all_space . '::XMLReader::_hidden::getline', );
1397             ###LogSD $phone->talk( level => 'debug', message => [
1398             ###LogSD "adding localized '<' as the newline character for \$/" ] );
1399             $self->$orig;
1400             };
1401              
1402             sub _reconcile_attribute_strings{
1403 3716     3716   3374 my( $self, $parse_ref ) = @_;
1404             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1405             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_reconcile_attribute_strings', );
1406             ###LogSD $phone->talk( level => 'debug', message => [
1407             ###LogSD "attempting to reconcile attribute split for list:", $parse_ref ] );
1408 3716         3058 my @attributes;
1409 3716         21034 my $should_glue = 0;
1410 3716         34894 my $test_string;
1411 3716         4188 for my $string ( @$parse_ref ){
1412             ###LogSD $phone->talk( level => 'debug', message =>[ "processing sting: $string" ] );
1413 8476 100       24155 if( $string =~ /^[^"]*"[^"]*$/ ){
    100          
    100          
1414             ###LogSD $phone->talk( level => 'warn', message =>[ "found unclosed quote" ] );
1415 113 100       161 if( $should_glue ){
1416             ###LogSD $phone->talk( level => 'warn', message =>[ "..which is a closing string" ] );
1417 88         271 $should_glue = 0;
1418 88         64756 push @attributes, $test_string . ' ' . $string;
1419 49         64 $test_string = undef;
1420             }else{
1421             ###LogSD $phone->talk( level => 'warn', message =>[ "..which is an opening string" ] );
1422 49         43 $should_glue = 1;
1423 49         67 $test_string = $string;
1424             }
1425             ###LogSD $phone->talk( level => 'warn', message =>[ "Updated attributes:", @attributes ] );
1426             }elsif( $should_glue ){
1427             ###LogSD $phone->talk( level => 'warn', message =>[ "found a middle string in an open sequence" ] );
1428 19         23 $test_string .= ' ' . $string;
1429             }elsif( length( $string ) == 0 ){
1430             ###LogSD $phone->talk( level => 'warn', message =>[ "Found a zero length string out in the open - don't add it" ] );
1431             }else{
1432             ###LogSD $phone->talk( level => 'debug', message =>[ "just a string: $string" ] );
1433 8071         9145 push @attributes, $string;
1434             }
1435             }
1436 3701 50       5354 if( $should_glue ){
1437 0         0 confess "Unable to close and open string with quotes -" . join '|~|', @attributes;
1438             }
1439             ###LogSD $phone->talk( level => 'trace', message =>[ "returning split:", @attributes ] );
1440 3701         7873 return @attributes;
1441             }
1442              
1443             sub _build_doctype_attributes{
1444 1     1   2 my( $self, $node_ref ) = @_;
1445             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1446             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_build_doctype_attributes', );
1447             ###LogSD $phone->talk( level => 'debug', message => [
1448             ###LogSD "attempting to build the DOCTYPE attributes for:", $node_ref] );
1449              
1450 1         4 $node_ref->{$node_ref->{attribute_strings}->[0]} = { $node_ref->{attribute_strings}->[1] => substr( $node_ref->{attribute_strings}->[2], 1, -1 ) };
1451             ###LogSD $phone->talk( level => 'debug', message =>[ "updated node ref:", $node_ref ] );
1452              
1453 1         2 return $node_ref;
1454             }
1455              
1456             sub _build_regular_attributes{
1457 3715     3715   3606 my( $self, $top_ref ) = @_;
1458             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1459             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_build_regular_attributes', );
1460             ###LogSD $phone->talk( level => 'debug', message => [
1461             ###LogSD "attempting to build an attributes ref for:", $top_ref ] );
1462              
1463 3715         2913 for my $att ( @{$top_ref->{attribute_strings}} ){
  3754         22798  
1464 8171 100 66     118879 next if !$att or $att eq 'xml:space="preserve"';
1465             ###LogSD $phone->talk( level => 'debug', message =>[ "parsing attribute string: $att" ] );
1466 8103         24746 my( $att_name, $att_val, $form_val ) = split /\s*=\s*/, $att;
1467             #~ $att_val = substr( $att_val, 1, -3 ) if substr( $att_val, 0, 1 ) eq '"';# Remove bracing quotes from values
1468 8112         63347 $att_val = $self->_remove_escapes( $att_val );
1469 8127         9513 $form_val = $self->_remove_escapes( $form_val );
1470             ###LogSD $phone->talk( level => 'debug', message =>[ "Final result:", $att_name, $att_val, $form_val ] );
1471 8127 50 66     131252 $att_val = substr( $att_val, 1, -1 ) if $att_val and (substr( $att_val, 0, 1 ) eq '"') and (substr( $att_val, -1, 1 ) eq '"');
      66        
1472 8112 100       11788 if( $att_name eq 'val' ){
    100          
1473             ###LogSD $phone->talk( level => 'debug', message =>[
1474             ###LogSD "found a value attribute" ] );
1475 615         1615 $top_ref->{$att_name} = $att_val;
1476             }elsif( $form_val ){
1477             ###LogSD $phone->talk( level => 'debug', message =>[
1478             ###LogSD "found a formula value: $form_val" ] );
1479             #~ $element->{attributes}->{$att_name} = '"' if substr( $form_val, -1, 1 ) eq '"';
1480 61 50       276 $top_ref->{attributes}->{$att_name} .=
1481             substr( $form_val, -1, 1 ) eq '"' ?
1482             substr( $form_val, 0, -1 ) : $form_val ;
1483             ###LogSD $phone->talk( level => 'debug', message =>[
1484             ###LogSD "final formula value: $top_ref->{attributes}->{$att_name}" ] );
1485             }else{
1486 7514         83942 $top_ref->{attributes}->{$att_name} = $att_val;
1487             }
1488             ###LogSD $phone->talk( level => 'debug', message =>[ "updated node ref:", $top_ref ] );
1489             }
1490              
1491 3739         4403 return $top_ref;
1492             }
1493              
1494             sub DEMOLISH{
1495 165     165 0 30112 my ( $self ) = @_;
1496             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1497             ###LogSD 'Spreadsheet::Reader::ExcelXML::XMLReader::_hidden::DEMOLISH', );# $self->get_all_space .
1498             ###LogSD $phone->talk( level => 'debug', message => [
1499             ###LogSD "XMLReader DEMOLISH called" ] );
1500              
1501 165         569 $self->close_the_file;
1502              
1503             }
1504              
1505             ###LogSD sub _print_current_file{ # Debugging method only used when the Log::Shiras debug source filter is on
1506 39         129 ###LogSD my ( $self, $ref ) = ( @_ );
1507 39         171 ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
1508             ###LogSD $self->get_all_space . '::XMLReader::_hidden::_print_current_file', );
1509 15         40988 ###LogSD my $line = ( caller(0) )[2];
1510 0 0         ###LogSD if( !$ref ){
1511 0           ###LogSD $phone->talk( level => 'debug', message =>[
1512             ###LogSD "The file handle sent from line -$line- is empty" ], );
1513             ###LogSD }else{
1514 0           ###LogSD $ref->seek( 0, 0 );
1515 0           ###LogSD my( $next_line, $print_string );
1516 0           ###LogSD while( $next_line = <$ref> ){
1517             #~ ###LogSD chomp( $next_line );
1518 0 0         ###LogSD next if $next_line =~ /^\s*$/;
1519 0           ###LogSD $print_string .= $next_line;
1520             ###LogSD }
1521             ###LogSD $phone->talk( level => 'debug', message =>[
1522             ###LogSD "For code line -$line- the file is:", $print_string ]);
1523             ###LogSD }
1524             ###LogSD }
1525              
1526             #########1 Phinish 3#########4#########5#########6#########7#########8#########9
1527              
1528 39     39   182194 no Moose;
  39         59  
  39         354  
1529              
1530             1;
1531              
1532             #########1 Documentation 3#########4#########5#########6#########7#########8#########9
1533             __END__
1534              
1535             =head1 NAME
1536              
1537             Spreadsheet::Reader::ExcelXML::XMLReader - A minimal pure-perl xml reader class
1538              
1539             =head1 SYNOPSIS
1540              
1541             package MyPackage;
1542             use MooseX::StrictConstructor;
1543             use MooseX::HasDefaults::RO;
1544             # You have to 'use' or build a the Workbook here or the XMLReader won't load
1545             # -> because the reader uses a regex to scrap imported methods
1546             use Spreadsheet::Reader::ExcelXML::Workbook;
1547             extends 'Spreadsheet::Reader::ExcelXML::XMLReader';
1548              
1549             =head1 DESCRIPTION
1550              
1551             This documentation is written to explain ways to use this module when writing your own
1552             excel spreadsheet parser. I suppose the class could be used more generally but that's
1553             not why I wrote it and for now I have no intention of providing a full xml toolbox.
1554             For Excel spreadsheet parsing generally please start at the top level documentation.
1555             L<Workbooks|Spreadsheet::Reader::ExcelXML>,
1556             L<Worksheets|Spreadsheet::Reader::ExcelXML::Worksheet>, and
1557             L<Cells|Spreadsheet::Reader::ExcelXML::Cell>.
1558              
1559             This class is meant to be used as the base reading class for specific types of xml
1560             files. The reader for those specific files will include roles that are useful for
1561             that files content. When the file first loads it will store some available information
1562             from the header (?) nodes and move to the first file node. At that point it will
1563             check if any of the consuming roles have a method '_load_unique_bits' If so it
1564             will call that method for additional meta data collection by that role.
1565              
1566             This class will process the xml file in a just in time fashion holding enough
1567             information to know the level and open nodes not yet closed but nothing else. The
1568             intent is to use a little RAM as possible and process the file in the most
1569             (pure perl) computationaly efficient way possible. I welcome all suggestions for
1570             improvement.
1571              
1572             =head2 Attributes
1573              
1574             Data passed to new when creating an instance. For modification of these attributes
1575             see the listed 'attribute methods'. For general information on attributes see
1576             L<Moose::Manual::Attributes>. For ways to manage the instance after it is opened
1577             see the L<Methods|/Methods>.
1578              
1579             =head3 file
1580              
1581             =over
1582              
1583             B<Definition:> This attribute holds the file handle for the file being read. If
1584             the full file name and path is passed to the attribute the class will coerce that
1585             into an L<IO::File> file handle.
1586              
1587             B<Default:> no default - this must be provided to read a file
1588              
1589             B<Required:> yes
1590              
1591             B<Range:> any unencrypted xml file name and path or IO::File file handle set to
1592             read.
1593              
1594             B<attribute methods> Methods provided to adjust this attribute
1595              
1596             =over
1597              
1598             B<set_file>
1599              
1600             =over
1601              
1602             B<Definition:> change the file value in the attribute (this will reboot
1603             the file instance and lock the file)
1604              
1605             =back
1606              
1607             B<get_file>
1608              
1609             =over
1610              
1611             B<Definition:> Returns the file handle of the file even if a file name
1612             was passed
1613              
1614             =back
1615              
1616             B<has_file>
1617              
1618             =over
1619              
1620             B<Definition:> this is used to see if the file loaded correctly.
1621              
1622             =back
1623              
1624             B<clear_file>
1625              
1626             =over
1627              
1628             B<Definition:> this clears (and unlocks) the file handle
1629              
1630             =back
1631              
1632             =back
1633              
1634             B<Delegated Methods>
1635              
1636             =over
1637              
1638             L<close|IO::Handle/$io-E<gt>close>
1639              
1640             =over
1641              
1642             closes the file handle
1643              
1644             =back
1645              
1646             L<seek|IO::Seekable/$io-E<gt>seek ( POS, WHENCE )>
1647              
1648             =over
1649              
1650             allows seek commands to be passed to the file handle
1651              
1652             =back
1653              
1654             L<getline|IO::Handle/$io-E<gt>getline>
1655              
1656             =over
1657              
1658             returns the next line of the file handle with '<' set as the
1659             L<input_record_separator ($E<sol>)|http://perldoc.perl.org/perlvar.html>
1660              
1661             =back
1662              
1663             =back
1664              
1665             =back
1666              
1667             =head3 workbook_inst
1668              
1669             =over
1670              
1671             B<Definition:> This attribute holds a reference to the top level workbook (parser).
1672             The purpose is to use some of the methods provided there.
1673              
1674             B<Default:> no default
1675              
1676             B<Required:> not strictly for this class but the attribute is provided to give
1677             self referential access to general workbook settings and methods for composed
1678             classes that inherit this a base class.
1679              
1680             B<Range:> isa => 'Spreadsheet::Reader::ExcelXML::Workbook'
1681              
1682             B<attribute methods> Methods provided to adjust this attribute
1683              
1684             =over
1685              
1686             B<set_workbook_inst>
1687              
1688             =over
1689              
1690             set the attribute with a workbook instance
1691              
1692             =back
1693              
1694             =back
1695              
1696             B<Delegated Methods (required)> Methods delegated to this module by the
1697             attribute. All methods are delegated with the method name unchanged.
1698             Follow the link to review documentation of the provider for each method.
1699             As you can see several are delegated through the Workbook level and
1700             don't originate there.
1701              
1702             =over
1703              
1704             L<Spreadsheet::Reader::ExcelXML/get_group_return_type>
1705              
1706             L<Spreadsheet::Reader::ExcelXML/counting_from_zero>
1707              
1708             L<Spreadsheet::Reader::ExcelXML/are_spaces_empty>
1709              
1710             L<Spreadsheet::Reader::ExcelXML/has_shared_strings_interface>
1711              
1712             L<Spreadsheet::Reader::ExcelXML/should_skip_hidden>
1713              
1714             L<Spreadsheet::Reader::ExcelXML/spreading_merged_values>
1715              
1716             L<Spreadsheet::Reader::ExcelXML/starts_at_the_edge>
1717              
1718             L<Spreadsheet::Reader::ExcelXML/get_empty_return_type>
1719              
1720             L<Spreadsheet::Reader::ExcelXML/get_values_only>
1721              
1722             L<Spreadsheet::Reader::ExcelXML/get_epoch_year>
1723              
1724             L<Spreadsheet::Reader::ExcelXML/get_error_inst>
1725              
1726             L<Spreadsheet::Reader::ExcelXML/has_styles_interface>
1727              
1728             L<Spreadsheet::Reader::ExcelXML/boundary_flag_setting>
1729              
1730             L<Spreadsheet::Reader::ExcelXML/is_empty_the_end>
1731              
1732             L<Spreadsheet::Reader::ExcelXML/get_rel_info>
1733              
1734             L<Spreadsheet::Reader::ExcelXML/get_sheet_info>
1735              
1736             L<Spreadsheet::Reader::ExcelXML/get_sheet_names>
1737              
1738             L<Spreadsheet::Reader::ExcelXML/collecting_merge_data>
1739              
1740             L<Spreadsheet::Reader::ExcelXML/collecting_column_formats>
1741              
1742             L<Spreadsheet::Reader::ExcelXML::Error/set_error( $error_string )>
1743              
1744             L<Spreadsheet::Reader::Format/get_defined_conversion( $position )>
1745              
1746             L<Spreadsheet::Reader::Format/set_defined_excel_formats( %args )>
1747              
1748             L<Spreadsheet::Reader::Format/parse_excel_format_string( $string, $name )>
1749              
1750             L<Spreadsheet::Reader::Format/change_output_encoding( $string )>
1751              
1752             L<Spreadsheet::Reader::ExcelXML::SharedStrings/get_shared_string( $positive_intE<verbar>$name )>
1753              
1754             L<Spreadsheet::Reader::ExcelXML::Styles/get_format( ($positionE<verbar>$name), [$header], [$exclude_header] )>
1755              
1756             =back
1757              
1758             =back
1759              
1760             =head3 xml_version
1761              
1762             =over
1763              
1764             B<Definition:> This stores the xml version read from the xml header. It is read
1765             when the file handle is first set in this sheet.
1766              
1767             B<Default:> no default - this is auto read from the header
1768              
1769             B<Required:> no
1770              
1771             B<Range:> xml versions
1772              
1773             B<attribute methods> Methods provided to adjust this attribute
1774              
1775             =over
1776              
1777             B<version>
1778              
1779             =over
1780              
1781             get the stored xml version
1782              
1783             =back
1784              
1785             =back
1786              
1787             =back
1788              
1789             =head3 xml_encoding
1790              
1791             =over
1792              
1793             B<Definition:> This stores the data encoding of the xml file from the xml header.
1794             It is read when the file handle is first set in this sheet.
1795              
1796             B<Default:> no default - this is auto read from the header
1797              
1798             B<Required:> no
1799              
1800             B<Range:> valid xml file encoding
1801              
1802             B<attribute methods> Methods provided to adjust this attribute
1803              
1804             =over
1805              
1806             B<encoding>
1807              
1808             =over
1809              
1810             get the attribute value
1811              
1812             =back
1813              
1814             B<has_encoding>
1815              
1816             =over
1817              
1818             predicate for the attribute value
1819              
1820             =back
1821              
1822             =back
1823              
1824             =back
1825              
1826             =head3 xml_progid
1827              
1828             =over
1829              
1830             B<Definition:> This is an attribute found in a secondary xml header that
1831             is associated with Excel 2003 xml based files. The value can be tested
1832             to see if the file was intended to be compliant with that format.
1833              
1834             B<Default:> no default - this is auto read from the header
1835              
1836             B<Required:> no
1837              
1838             B<Range:> a string
1839              
1840             B<attribute methods> Methods provided to adjust this attribute
1841              
1842             =over
1843              
1844             B<progid>
1845              
1846             =over
1847              
1848             get the attribute value
1849              
1850             =back
1851              
1852             B<has_progid>
1853              
1854             =over
1855              
1856             predicate for the attribute value
1857              
1858             =back
1859              
1860             =back
1861              
1862             =back
1863              
1864             =head3 xml_header
1865              
1866             =over
1867              
1868             B<Definition:> This stores the primary xml header string from the xml file. It
1869             is read when the file handle is first set in this sheet. I contains both the
1870             verion and the encoding where available and is used when building subsets of
1871             the file as standalone xml.
1872              
1873             B<Default:> no default - this is auto read from the header
1874              
1875             B<Required:> no
1876              
1877             B<Range:> valid xml file header
1878              
1879             B<attribute methods> Methods provided to adjust this attribute
1880              
1881             =over
1882              
1883             B<get_header>
1884              
1885             =over
1886              
1887             get the attribute value
1888              
1889             =back
1890              
1891             B<_set_xml_header>
1892              
1893             =over
1894              
1895             set the attribute value
1896              
1897             =back
1898              
1899             =back
1900              
1901             =back
1902              
1903             =head3 xml_doctype
1904              
1905             =over
1906              
1907             B<Definition:> This stores the DOCTYPE indicated in the XML header !DOCTYPE
1908              
1909             B<Default:> no default - this is auto read from the header
1910              
1911             B<Required:> no
1912              
1913             B<Range:> whatever it finds
1914              
1915             B<attribute methods> Methods provided to adjust this attribute
1916              
1917             =over
1918              
1919             B<doctype>
1920              
1921             =over
1922              
1923             get the attribute value
1924              
1925             =back
1926              
1927             B<has_doctype>
1928              
1929             =over
1930              
1931             predicate for the attribute
1932              
1933             =back
1934              
1935             =back
1936              
1937             =back
1938              
1939             =head3 position_index
1940              
1941             =over
1942              
1943             B<Definition:> This attribute is available to facilitate other consuming roles and
1944             classes. Of this attributes methods only the 'clear_location' method is used in this
1945             class during the L<start_the_file_over|/start_the_file_over> method. It can be used
1946             for tracking positions with the same node name.
1947              
1948             B<Default:> no default - this is mostly managed by the role or child class
1949              
1950             B<Required:> no
1951              
1952             B<Range:> Integer
1953              
1954             B<attribute methods> Methods provided to adjust this attribute
1955              
1956             =over
1957              
1958             B<where_am_i>
1959              
1960             =over
1961              
1962             get the attribute value
1963              
1964             =back
1965              
1966             B<i_am_here>
1967              
1968             =over
1969              
1970             set the attribute value
1971              
1972             =back
1973              
1974             B<clear_location>
1975              
1976             =over
1977              
1978             clear the attribute value
1979              
1980             =back
1981              
1982             B<has_position>
1983              
1984             =over
1985              
1986             set the attribute value
1987              
1988             =back
1989              
1990             =back
1991              
1992             =back
1993              
1994             =head3 file_type
1995              
1996             =over
1997              
1998             B<Definition:> This is a static attribute that shows the file type
1999              
2000             B<Default:> xml
2001              
2002             B<attribute methods> Methods provided to adjust this attribute
2003              
2004             =over
2005              
2006             B<get_file_type>
2007              
2008             =over
2009              
2010             get the attribute value
2011              
2012             =back
2013              
2014             =back
2015              
2016             =back
2017              
2018             =head3 stacking
2019              
2020             =over
2021              
2022             B<Definition:> a pure perl xml parser will in general be slower than the C equivalent.
2023             To provide some acceleration to arrive at a target destination you can turn of the stack
2024             trace which will include building and storing the trace elements. This breaks things so
2025             don't do it without a solid understanding of what is happening. For instance if you turn
2026             this off and then call the method L<parse_element|/parse_element( [$depth] )> The
2027             parse_element method will have to turn the stack trace back on on it's own to build the
2028             element tree. The issue is that the most recent element at the base of the tree won't be
2029             available to build from. You will need to manually build it and push it to the stack. See
2030             the methods L<initial_node_build|/initial_node_build( $node_name, $attribute_list_ref )> and
2031             L<add_node_to_stack|/add_node_to_stack( $node_ref )> to implement this.
2032              
2033             B<Default:> 1 = the stack trace is on
2034              
2035             B<attribute methods> Methods provided to adjust this attribute
2036              
2037             =over
2038              
2039             B<should_be_stacking>
2040              
2041             =over
2042              
2043             get the attribute value
2044              
2045             =back
2046              
2047             B<change_stack_storage_to( $Bool )>
2048              
2049             =over
2050              
2051             Turn the stack trace(r) state to $Bool (1 = on)
2052              
2053             =back
2054              
2055             =back
2056              
2057             =back
2058              
2059             =head2 Methods
2060              
2061             These are the methods provided by this class.
2062              
2063             =head3 start_the_file_over
2064              
2065             =over
2066              
2067             B<Definition:> Clears the L<position_index|/position_index>, the old stack trace, and kick starts
2068             L<stack trace tracking|/stacking> again. It then uses seek(0, 0) to reset the file handle to the
2069             beginning. Finally, it reads the file until it gets to the first non-xml header node.
2070              
2071             B<Accepts:> nothing
2072              
2073             B<Returns:> nothing
2074              
2075             =back
2076              
2077             =head3 good_load( $state )
2078              
2079             =over
2080              
2081             B<Definition:> a setter method to indicated if the file loaded correctly. This
2082             generally should be set by consuming roles in the L<load_unique_bits
2083             |/load_unique_bits> phase.
2084              
2085              
2086             B<Accepts:> (1|0)
2087              
2088             B<Returns:> nothing
2089              
2090             =back
2091              
2092             =head3 loaded_correctly
2093              
2094             =over
2095              
2096             B<Definition:> a getter method to understand if the file loaded correctly.
2097             This is generally used by consumers of the instance to see if there was any
2098             trouble during the initial build.
2099              
2100             B<Accepts:> nothing
2101              
2102             B<Returns:> 1 = good build, 0 = bad_build
2103              
2104             =back
2105              
2106             =head3 parse_element( [$depth] )
2107              
2108             =over
2109              
2110             B<Definition:> This will read and store the full node from the current position
2111             down to an optional $depth. When the parse is complete the parser will be
2112             positioned at the beginning of the next node. The node does not include the
2113             top name but will include attributes.
2114              
2115             B<Accepts:> $depth = optional
2116              
2117             B<Returns:> A perl hash reference where all nodes at a level are listed using three
2118             hashref keys; list_keys, list, and attributes. The 'attributes' key points to a
2119             hash reference containing that nodes attributes. The 'list_keys' key points to an
2120             array reference with all the node names for each node at the next level down. The
2121             'list' key points to an array reference of nodes or node values matching the position
2122             of the list_keys. There are two special case exceptions to this. First, for text
2123             values the node is listed as { raw_text => 'text node content' }. Second, if the
2124             attributes only include a 'val' key the node stores this under the 'val' key rather
2125             than the 'attributes' key with a sub key 'val'.
2126              
2127             =back
2128              
2129             =head3 advance_element_position( $element, [$iterations] )
2130              
2131             =over
2132              
2133             B<Definition:> This will move the xml file reader forward until it finds the identified named
2134             $element. If the reader is already at an element of that name it will index forward until it finds
2135             the next $element of that name. If the optional positive $iterations integer is passed it will index
2136             to the named $element - $iterations times.
2137              
2138             B<Accepts:> $element = a case sensitive xml node name found forward of the
2139             current position in the file. [$iterations] = optional a positive integer
2140             indicating how many times to index forward to the named $element.
2141              
2142             B<Returns:> a list of 4 positions ( $success, $node_name, $node_level, $return_node_ref )
2143              
2144             $success = a boolean value indicating whether the desired goal was met, $node_name = the actual node
2145             name for the final position (should match $element if $success), $node_level = the level of the final
2146             named node in the stack( not the sub text node ) $return_node_ref = When the L<stacking|/stacking>
2147             attribute is on this returns the last displaced elements in the stack displaced by the traverse of
2148             the xml tree. When stacking is off this returns an array ref of values used as the second argument in
2149             L<initial_node_build|/initial_node_build( $node_name, $attribute_list_ref )>.
2150              
2151             =back
2152              
2153             =head3 next_sibling
2154              
2155             =over
2156              
2157             B<Definition:> This will move the xml file reader forward until it finds next
2158             node at the same level as the current node within the same supernode. If this
2159             method finds a higher node prior to finding a node at the same level it will
2160             return failure and stop reading.
2161              
2162             B<Accepts:> nothing
2163              
2164             B<Returns:> a list of 4 positions ( $success, $node_name, $node_level, $return_node_ref )
2165              
2166             $success = a boolean value indicating whether the desired goal was met, $node_name = the actual node
2167             name for the final position (should match $element if $success), $node_level = the level of the final
2168             named node in the stack( not the sub text node ) $return_node_ref = When the L<stacking|/stacking>
2169             attribute is on this returns the last displaced elements in the stack displaced by the traverse of
2170             the xml tree. When stacking is off this returns an array ref of values used as the second argument in
2171             L<initial_node_build|/initial_node_build( $node_name, $attribute_list_ref )>.
2172              
2173             =back
2174              
2175             =head3 skip_siblings
2176              
2177             =over
2178              
2179             B<Definition:> This will move the xml file reader forward until it finds next
2180             node higher. It will not stop on end nodes so it will continue to pass all
2181             closed nodes until it comes to the first open or self contained node above
2182             the current node.
2183              
2184             B<Accepts:> nothing
2185              
2186             B<Returns:> a list of 4 positions ( $success, $node_name, $node_level, $return_node_ref )
2187              
2188             $success = a boolean value indicating whether the desired goal was met, $node_name = the actual node
2189             name for the final position (should match $element if $success), $node_level = the level of the final
2190             named node in the stack( not the sub text node ) $return_node_ref = When the L<stacking|/stacking>
2191             attribute is on this returns the last displaced elements in the stack displaced by the traverse of
2192             the xml tree. When stacking is off this returns an array ref of values used as the second argument in
2193             L<initial_node_build|/initial_node_build( $node_name, $attribute_list_ref )>.
2194              
2195             =back
2196              
2197             =head3 current_named_node
2198              
2199             =over
2200              
2201             B<Definition:> when processing xml files in a just in time fashion there
2202             will be some ambiguity surrounding text nodes;
2203              
2204             <t>sometext</t>
2205             <s>
2206             <r val="2"/>
2207              
2208             In the 't' node example the content between the '>' character and the '<'
2209             characters are intentional and valuable to the data set. In the 's' and
2210             'r' node example the space between those characters is only intended for
2211             human readability. This parser will not be able to tell the value of the
2212             content after the 's' node '>' character until the 'r' node is read. At
2213             that point the 's' node will no longer be the 'current' position. To
2214             resolve this, all content other than '' between '>' and '<' is treated as
2215             a node until the next node is read. Because these nodes are ambiguous
2216             the idea of a 'named node' is valuable and knowing what the most recent
2217             named node is can be useful. This method either returns the last read node
2218             or the second to last node if the last node is a raw text node. In the
2219             first example it would return the 't' node and in the second example it
2220             would return the 's' node.
2221              
2222             B<Accepts:> nothing
2223              
2224             B<Returns:> a hash ref of information about the node containing the
2225             following keys;
2226              
2227             level => counting from 0 at the start of the file and moving up
2228             type => regular = xml named node|#text = node built from the contents between the > and < characters
2229             name => the xml node name (for #text nodes this is 'raw_text')
2230             closed => (closed|open) depending on the current tag state
2231             initial_string => The string inside the < > quotes prior to parsing
2232             [attributes] => all attributes and values will be stored under the attribute name
2233             [val] => special case storage of one attribute
2234              
2235             =back
2236              
2237             =head3 squash_node( $node )
2238              
2239             =over
2240              
2241             B<Definition:> This takes a $node from the L<parse_element|/parse_element> output
2242             and turns it into a more perl like reference. It checks the list_keys and if
2243             there are any duplicates it takes the list values and uses them as elements of
2244             an array ref assigned to a hash key called list. If there are no duplicates
2245             in the list_keys it turns the list_keys into hash keys with the list elements
2246             assigned as values. It then takes the attributes and mingles them in the hashref
2247             with the prior results. There are two special cases for a node reorganization.
2248             For nodes with a 'val' in the 'list_keys' then the element in the same position of
2249             the 'list' is returned as the whole ref. If there is a raw_text node it is returned
2250             as a hashref with one key 'raw_text' with the text itself as the value. This
2251             is all done recursivly so lower layers are assigned to upper layers using the
2252             rules above.
2253              
2254             B<Accepts:> the output of a L<parse_element|/parse_element> call
2255              
2256             B<Returns:> a perl data structure with the xml organization removed
2257              
2258             =back
2259              
2260             =head3 extract_file( @node_list )
2261              
2262             =over
2263              
2264             B<Definition:> This will build an xml file and load it to a L<IO::Handle>-E<gt>new_tmpfile
2265             object. The xml is built on whole extracted xml strings defined by @node_list.
2266             If none of the node list elements is found in the parsed file then the first
2267             listed element from the node list will be used to create an empty self closing
2268             node.
2269              
2270             B<Accepts:> @node_list = Node list items can either be xml node name strings or array refs
2271             composed of two elements, first the node name and second the iterated position. Ex.
2272              
2273             @node_list_example = ( 'r', [ 'si', 3 ] );
2274              
2275             In this example the extracted file would contain the first 'r' node and the 3rd
2276             'si' node.the output of a L<parse_element|/parse_element> call. There is the
2277             exception case where you just want the whole file passed. The out here is to
2278             pass 'ALL_FILE' as the first element of the @node_list and a complete copy of
2279             the file_handle in read mode will be passed.
2280              
2281             B<Returns:> a File::Temp file handle loaded with an xml header and the listed
2282             nodes.
2283              
2284             =back
2285              
2286             =head3 current_node_parsed
2287              
2288             =over
2289              
2290             B<Definition:> When nodes are read they are not completely processed to save
2291             cycles. If you want a fully processed result from the current node position
2292             including any embedded text then this is the method for you.
2293              
2294             B<Accepts:> Nothing
2295              
2296             B<Returns:> a perl ref equivalent to the squash_node call. This only returns the
2297             fully processed current_named_node and any sub text nodes.
2298              
2299             =back
2300              
2301             =head3 close_the_file
2302              
2303             =over
2304              
2305             B<Definition:> It may be that the file(handle) may not be needed during the whole
2306             workbook parse. If so you can use this method to close (and clear / release) an
2307             open file handle as appropriate.
2308              
2309             B<Accepts:> Nothing
2310              
2311             B<Returns:> Nothing (the file handle is closed and cleared)
2312              
2313             =back
2314              
2315             =head3 not_end_of_file
2316              
2317             =over
2318              
2319             B<Definition:> This is a poor mans End Of File test (EOF). The reader builds
2320             a node stack to keep track of where it is in the xml parse and when it runs out
2321             of nodes it means you are back at the top of the stack.
2322              
2323             B<Accepts:> Nothing
2324              
2325             B<Returns:> a count of the nodes in the node stack (header nodes are processed
2326             early on and are read and removed as part of startup)
2327              
2328             =back
2329              
2330             =head3 initial_node_build( $node_name, $attribute_list_ref )
2331              
2332             =over
2333              
2334             B<Definition:> Generally this is an internal method and should not be used. However,
2335             in order to provide a faster forward ability the node stack trace(ing) can be
2336             L<turned off|stacking>. When you want to turn it back on you have to manually build
2337             the top node using this method and store it to the node stack using L<add_node_to_stack
2338             |/add_node_to_stack( $node_ref )>. This method will build the essentials for adding
2339             to the node stack. Please not that it will not necessarily get the node level right.
2340             I<If you need that to be correct then don't turn off the stack trace.> It will not
2341             build raw_text nodes correctly.
2342              
2343             B<Accepts:>
2344             $node_name = a string without spaces for the name of the node,
2345             $attribute_list_ref = This is basically everything else in the xml tag except the name
2346             split on /\s+/. Any self closing '/' should be removed prior to the split.
2347              
2348             B<Returns:> a node ref that can be added to the node stack to kickstart stack tracing
2349              
2350             =back
2351              
2352             =head3 add_node_to_stack( $node_ref )
2353              
2354             =over
2355              
2356             B<Definition:> Generally this is an internal method and should not be used. However,
2357             in order to provide a faster forward ability the node stack trace(ing) can be
2358             L<turned off|stacking>. When you want to turn it back on you have to manually build
2359             the top node and store it to the node stack using this method. Adding a node after the stack
2360             trace has been turned off will create a discontinuity where the new node is added. Stack
2361             trace operations above this node will generally fail and stop the script.
2362              
2363             B<Accepts:> $node_ref = a top to push on the node stack for traceability
2364              
2365             B<Returns:> nothing
2366              
2367             =back
2368              
2369             =head1 SUPPORT
2370              
2371             =over
2372              
2373             L<github Spreadsheet::Reader::ExcelXML/issues
2374             |https://github.com/jandrew/p5-spreadsheet-reader-excelxml/issues>
2375              
2376             =back
2377              
2378             =head1 TODO
2379              
2380             =over
2381              
2382             B<1.> Nothing currently
2383              
2384             =back
2385              
2386             =head1 AUTHOR
2387              
2388             =over
2389              
2390             =item Jed Lund
2391              
2392             =item jandrew@cpan.org
2393              
2394             =back
2395              
2396             =head1 COPYRIGHT
2397              
2398             This program is free software; you can redistribute
2399             it and/or modify it under the same terms as Perl itself.
2400              
2401             The full text of the license can be found in the
2402             LICENSE file included with this module.
2403              
2404             This software is copyrighted (c) 2016 by Jed Lund
2405              
2406             =head1 DEPENDENCIES
2407              
2408             =over
2409              
2410             L<Spreadsheet::Reader::ExcelXML> - the package
2411              
2412             =back
2413              
2414             =head1 SEE ALSO
2415              
2416             =over
2417              
2418             L<Spreadsheet::Read> - generic Spreadsheet reader
2419              
2420             L<Spreadsheet::ParseExcel> - Excel binary version 2003 and earlier (.xls files)
2421              
2422             L<Spreadsheet::XLSX> - Excel version 2007 and later
2423              
2424             L<Spreadsheet::ParseXLSX> - Excel version 2007 and later
2425              
2426             L<Log::Shiras|https://github.com/jandrew/Log-Shiras>
2427              
2428             =over
2429              
2430             All lines in this package that use Log::Shiras are commented out
2431              
2432             =back
2433              
2434             =back
2435              
2436             =cut
2437              
2438             #########1#########2 main pod documentation end 5#########6#########7#########8#########9