File Coverage

blib/lib/Pod/Simple/Select.pm
Criterion Covered Total %
statement 133 326 40.8
branch 25 152 16.4
condition 0 9 0.0
subroutine 27 45 60.0
pod n/a
total 185 532 34.7


line stmt bran cond sub pod time code
1             package Pod::Simple::Select;
2 1     1   15369 use strict;
  1         3  
  1         23  
3 1     1   4 use warnings;
  1         2  
  1         39  
4            
5             =head1 NAME
6            
7             Pod::Simple::Select - Select parts in a file using pod directives
8            
9             =head1 VERSION
10            
11             Version 0.001
12            
13             =cut
14            
15             our $VERSION = '0.001';
16            
17 1     1   4 use base qw(Pod::Simple);
  1         6  
  1         528  
18 1     1   23547 use Fcntl 'O_RDONLY';
  1         3  
  1         54  
19 1     1   1187220 use Tie::File;
  1         13854  
  1         41  
20            
21             #use Carp qw/confess/;
22 1     1   715 use Log::Log4perl;
  1         35618  
  1         6  
23 1     1   554 use Data::Dumper;
  1         4893  
  1         270  
24            
25             my $end_line;
26             my $key;
27             my $h_value;
28             #my $token_match;
29             my $token;
30            
31             #my $last_end;
32            
33             my $conf = q(
34             log4perl.rootLogger=ERROR, LOGFILE1, Screen
35             #log4perl.rootLogger=DEBUG, LOGFILE1, Screen
36             #log4perl.logger.Pod.Simple.Select=DEBUG, LOGFILE1, Screen
37             #log4perl.logger.Pod.Simple.Select.Token=DEBUG, LOGFILE1, Screen
38             log4perl.appender.LOGFILE1=Log::Log4perl::Appender::File
39             log4perl.appender.LOGFILE1.filename=./debug.log
40             log4perl.appender.LOGFILE1.mode=clobber
41             log4perl.appender.LOGFILE1.layout=PatternLayout
42             log4perl.appender.LOGFILE1.Threshold=DEBUG
43             log4perl.appender.LOGFILE1.layout.ConversionPattern= %c{1}-%M{1} %m%n
44            
45             log4perl.appender.Screen=Log::Dispatch::Screen
46             log4perl.appender.Screen.stderr=0
47             log4perl.appender.Screen.Threshold=DEBUG
48             log4perl.appender.Screen.mode=append
49             log4perl.appender.Screen.layout=Log::Log4perl::Layout::PatternLayout
50             log4perl.appender.Screen.layout.ConversionPattern=%c{1}-%M{1} %m%n
51             );
52             Log::Log4perl->init( \$conf );
53            
54             =head1 SYNOPSIS
55            
56             use Pod::Simple::Select;
57            
58             my $p = Pod::Simple::Select->new;
59             $p->output_file("out");
60             $p->select(["head1" =>["Top1"=>["head2"=>["Top12"]]], "head2" =>["Function doit"], "head3"=>["Top3"]]);
61             $p->parse_file(ex.pod);
62            
63             Given that the ex.pod file is
64            
65             =head1 NotFound
66            
67             Bla
68             Bla
69             Bla
70            
71             =head1 NotFound
72            
73             Bla
74             Bla
75             Bla
76            
77             =head1 Top1
78            
79             Bla under top1
80             Bla2 under top1
81             Bla3 under top1
82            
83             =head2 Top12
84            
85             Bla under top12
86             Bla2 under top12
87             Bla3 under top12
88            
89             =cut
90            
91             code
92             code
93             code
94            
95             =head2 Function C
96            
97             Bla under Function Doit
98             Bla2 under Function Doit
99             Bla3 under Function Doit
100            
101             =head2 Top12
102            
103             Bla under top12_2
104             Bla2 under top12_2
105             Bla3 under top12_2
106            
107             =head1 NotFound
108            
109             Bla
110             Bla
111             Bla
112            
113             =head3 Top3
114            
115             Bla under top3
116            
117             =head1 NotFound
118            
119             Bla
120             Bla
121             Bla
122            
123             The out file will be
124            
125             =head2 Top12
126            
127             Bla under top12
128             Bla2 under top12
129             Bla3 under top12
130            
131             =head2 Function C
132            
133             Bla under Function Doit
134             Bla2 under Function Doit
135             Bla3 under Function Doit
136            
137             =head3 Top3
138            
139             Bla under top3
140            
141             =head1 DESCRIPTION
142            
143            
144             This module will extract specified sections of
145             pod documentation from a file. This ability is provided by the
146             B
147             POD sections to select for processing/printing.
148            
149             =head1 SECTION SPECIFICATIONS
150            
151             The argument to C
152             An array reference containing array refereces will restrict the text processed to only the
153             desired set of sections, or subsections following a section.
154            
155             Pod directive is head1, head2 and so on.
156            
157             The formal syntax of a section specification is:
158            
159             =over 4
160            
161             =item * Ordered parsing
162            
163             ["Pod directive" =>["Text following the directive"=>["Pod directive 2"=>["Text2"]], "Text3], "Pod directive 3" => [Text4], ...]
164            
165             A list of token will be made using these array references and that list will be used for the parsing.
166             A key (:text after a pod directive) not found in the file will stop the parser from searching further down.
167             If an array references are enclosed within each other, the parsing will search for a sequence in the same order.
168            
169             =item * Unordered parsing
170            
171             {"Pod directive" =>["Text following the directive", "Text2", "Text3], "Another pod directive => [Text4], ...}
172            
173             A list of token is constructed during the parsing, and all the Pod directive and key are on the same level.
174             The order in the hash has no meaning. A pod directive or a key given in the hash and not found in the file won't stop the parsing.
175            
176             =back
177            
178             =cut
179            
180             sub new {
181 1     1   17 my $self = shift;
182 1         13 my $new = $self->SUPER::new(@_);
183 1         42 $new->{log} = Log::Log4perl->get_logger(__PACKAGE__);
184            
185             #$new->cut_handler( \&cut_seen );
186 1         477 $new->{output_as_hash} = 0;
187 1         3 return $new;
188            
189             }
190            
191             =head1 B
192            
193             $parser->select(["head1"=>["select()", "output_hash"]]);
194            
195             $parser->select({head1 =>["select()", "output_hash"]});
196            
197             The first call will search for the pod section in that order.
198             The second call will catch the two section in any order.
199            
200             $parser->select(["head1" => ["select()", ["head1"=>["output_hash()"]]]]);
201            
202             This will search for a C<<=head1 B >> pod secttion following a C<< =head1 B >> section.
203            
204             =cut
205            
206             sub select {
207 1     1   520 my ( $self, $ar_r ) = @_;
208 1 50       6 if ( ref $ar_r eq "ARRAY" ) {
    0          
209 1         5 $self->make_tree( undef, undef, $ar_r );
210            
211             #add a last token as a sentinelle so that the
212             #whole file is parse and the last line of
213             #the last token to fetch is found correctly
214 1         6 Pod::Simple::Select::Token->new( $self->{doc}, "Document_end" );
215 1         5 my $next = $self->{doc}->child_at(0);
216 1 50       7 $self->link_tree( $self->{doc}, $next ) if ($next);
217 1         9 $self->cut_handler( \&cut_seen );
218             }
219             elsif ( ref $ar_r eq "HASH" ) {
220            
221 0         0 my @pods;
222             my @keys_r;
223 0         0 for my $key ( keys %$ar_r ) {
224 0         0 push @pods, $key;
225 0         0 my $k_r = $ar_r->{$key};
226 0         0 my @k_p;
227 0         0 for my $key (@$k_r) {
228 0         0 push @k_p, qr/\b$key\b/i;
229             }
230 0         0 push @keys_r, \@k_p;
231            
232             }
233 0         0 $self->{doc} = Pod::Simple::Select::Token->new( undef, "doc" );
234 0         0 $self->{doc}->set_pod_pattern( \@pods );
235 0         0 $self->{doc}->{key_pat} = \@keys_r;
236 0         0 $self->cut_handler( \&cut_seen_uo );
237            
238             {
239 1     1   6 no warnings "redefine";
  1         2  
  1         1509  
  0         0  
240            
241 0         0 *_handle_element_start = \&_handle_element_start_uo;
242 0         0 *_handle_text = \&_handle_text_uo;
243            
244             }
245             }
246             }
247            
248             #A recursive method for creating a list of token using the array ref given in select
249             #param: $token the parent token,
250             # $pod_dir the directive given in array ref for the token
251             # $ar_r the array ref from select
252             #Use the parent child relation
253            
254             sub make_tree {
255 1     1   4 my ( $self, $token, $pod_dir, $ar_r ) = @_;
256 1         2 my $child_token;
257 1 50       10 if ( !defined $token ) {
258 1         10 $token = Pod::Simple::Select::Token->new( undef, "doc" );
259 1         3 $self->{doc} = $token;
260             }
261 1         2 my $pod_sel;
262 1         3 for my $val ( @{$ar_r} ) {
  1         3  
263 2 100       6 if ( ref $val eq "ARRAY" ) {
264 1         3 for my $key (@$val) {
265 2 50       6 if ( ref $key eq "ARRAY" ) {
266 0         0 $self->make_tree( $child_token, $pod_sel, $key );
267             }
268             else {
269             #print "make_tree 3 ", $key, "\n";
270 2         5 $child_token =
271             Pod::Simple::Select::Token->new( $token, $pod_sel );
272 2         6 $child_token->set_key($key);
273             #print $child_token->get_level, "\n";
274             }
275             } #for
276             } #if
277             else {
278 1         4 $pod_sel = $val;
279             }
280             } #for
281             }
282            
283             #A recursive method for creating a list of token using the array ref given in select
284             #param: $parent the parent token,
285             # $first_child
286             #Create the next and previous relations.
287             #a parent's next is the first child
288             #a child's next is it's brother (or sister ... it's parent's next child)
289             #The last child is the parent's brother :
290             #that is: the next token of a leaf child is its neigbhour or the the parent neighbour
291             #In ->select the last token received a sentinelle token with pod dir "end_document"
292             #so that the parsing can terminate with undefined token
293            
294             sub link_tree {
295 4     4   10 my ( $self, $parent, $first_child ) = @_;
296 4 100       10 return unless $first_child;
297             $self->{log}->debug(
298 3         6 "link_tree previous: ", $parent->get_key,
299             " next: ", $first_child->get_key
300             );
301 3         25 $parent->next($first_child);
302 3         10 $first_child->previous($parent);
303 3         4 my $next;
304 3         8 for my $t ( $parent->children ) {
305 3 50       7 if ( $t->children_count ) {
306 0         0 $self->{log}->debug( $t->get_key, " has ", $t->children_count,
307             " children" );
308 0         0 $next = $t->child_at(0);
309             }
310             else {
311 3         7 $next = $parent->child_at( $t->next_index );
312             }
313 3         12 $self->link_tree( $t, $next );
314             }
315             }
316            
317             #For debugging: print the tree of token
318            
319             sub print_tree {
320 0     0   0 my $self = shift;
321 0         0 my $token = $self->{doc};
322 0         0 my $last;
323 0         0 print "print_tree\n";
324 0 0       0 if ( ref $token->{pod_pat} eq "Regexp" ) {
325 0         0 print $token->{pod_pat};
326 0         0 return;
327             }
328 0         0 while ($token) {
329 0         0 my $pat = $token->get_key_pattern;
330 0         0 print "\t" x $token->get_level, " ";
331 0 0       0 print "", ( defined $token->get_pod_pattern ? @{ $token->get_pod_pattern } : ""), " ";
  0         0  
332 0 0 0     0 print "", ( defined $pat && defined $pat->[0] ? $pat->[0]->[0] : " key pat undef" ), "\n";
333 0         0 $last = $token;
334 0         0 $token = $token->next;
335             }
336            
337             }
338            
339             =head1 B
340            
341             $parser->ouptut_hash
342            
343             Calling this method before calling C<$p->parse_file($filename)> will have parse_file return a the parsing in hash.
344             The keys are the text after the pod directives (followed by a counter if the same text is met more than once.
345            
346             =cut
347            
348             sub output_hash {
349 1     1   16 my $self = shift;
350 1         2 my $out;
351             open( $out, ">", \$h_value )
352 1 50   1   8 or $self->{log}->logdie("Can't set a scalar ref as a file handle $!");
  1         2  
  1         8  
  1         34  
353 1         754 $self->SUPER::output_fh($out);
354 1         14 $self->{output_as_hash} = 1;
355             }
356            
357             =head1 B
358            
359             $parser->output_file("selected_pod.txt");
360            
361             Will write the file with the pod sections selected in the parsing.
362            
363             =cut
364            
365             sub output_file {
366 0     0   0 my ( $self, $file ) = @_;
367 0         0 my $fh;
368 0 0       0 if ($file) {
369             open $fh, ">", $file
370 0 0       0 or $self->{log}->logdie("Can't open $file for writing $!");
371             }
372             else {
373 0         0 $fh = *STDOUT{IO};
374             }
375 0         0 $self->SUPER::output_fh($fh);
376            
377             }
378            
379             =head1 B
380            
381             $parser->parse_file("Select.pm");
382            
383             This method run the parsing. It as to be called after C<$p->select(...)> and C>$p->output_file(...)> or C<$p->output_hash()>.
384            
385             =cut
386            
387             sub parse_file {
388 1     1   8 my ( $self, $file ) = @_;
389 1         10 tie my @array, 'Tie::File', $file, mode => O_RDONLY;
390             $self->{log}->logconfess(
391 1 50       153 "$file is seen as one line long by Tie::File\nPlease set the line ending according to your OS"
392             ) if ( scalar(@array) == 1 );
393            
394             #$token_match = $self->{doc};
395 1         4 $token = $self->{doc}->next;
396             # $self->{doc}->_print_patt;
397            
398 1         8 $self->SUPER::parse_file($file);
399            
400 0 0       0 if ($token->get_key eq "doc_Document_end_") { #ordered parsing, last token is the sentinel
401 0 0       0 $token->previous->line_end( @array + 0) unless $token->previous->line_end;
402             }
403             else { #unordered parsing: last token is the current token
404 0 0       0 $token->line_end( @array + 0) unless $token->line_end;
405             }
406 0         0 my $out = $self->{'output_fh'};
407 0         0 my %data;
408 0         0 $token = $self->{doc}->next;
409 0         0 my $key_count;
410 0         0 while ($token) {
411 0 0       0 $token = $token->next if ( $token->children_count );
412 0         0 my $key = $token->get_key;
413 0         0 $key =~s/^.*_//; #keep only the last part of the key
414             #array starts at 0 and line numbers starts at 1 ...
415 0         0 my $first = $token->line_start() - 1;
416 0         0 my $last = $token->line_end() - 1;
417 0         0 $self->{log}->debug( "key : ", $key, " ", $token->line_start(),
418             " - ", $token->line_end() );
419 0         0 for my $i ( $first .. $last ) {
420 0         0 print $out $array[$i], "\n";
421             }
422 0 0       0 if ( $self->{output_as_hash} ) {
423 0         0 while ( exists $data{$key} ) {
424 0         0 $key_count++;
425 0         0 $key .= " $key_count";
426             }
427 0         0 $data{$key} = $h_value;
428 0         0 $h_value = undef;
429 0         0 close $out;
430             open $out, ">", \$h_value
431 0 0       0 or $self->{log}->logdie("Can't open string for writing $!");
432             }
433 0         0 $token = $token->next;
434             }
435 0         0 close $out;
436 0         0 untie @array;
437 0 0       0 return %data if ( $self->{output_as_hash} );
438             }
439            
440             #
441             #A handler to get the =cut positions in the file
442             #
443            
444             sub cut_seen {
445 0     0   0 my ( $line, $line_number, $self ) = @_;
446             #my $mytoken = ( $token ? $token->previous : $token_match );
447 0         0 my $mytoken = $token->previous;
448             $self->{log}->debug(
449 0         0 $line_number, " token: ",
450             $mytoken->get_key, " line_end: ", $mytoken->line_end,
451             " replaced by ",
452             $line_number - 1
453             );
454 0 0       0 $mytoken->line_end( $line_number - 1 ) unless ( $mytoken->line_end );
455             }
456            
457             #
458             #The same handle for select args given in a hash ref (uo is for unordered)
459             #
460            
461             sub cut_seen_uo {
462 0     0   0 my ( $line, $line_number, $self ) = @_;
463             $self->{log}->debug(
464 0 0       0 $line_number,
465             " token: ",
466             ( $token
467             ? $token->get_key . " line_end: " . $token->line_end
468             : " undef"
469             )
470             );
471 0 0       0 $token->line_end( $line_number - 1 ) unless ($token->line_end);
472            
473             }
474            
475             sub _handle_element_start {
476 0     0   0 my ( $self, $e_name, $attr_hr ) = @_;
477            
478             #return unless $token;
479             return
480 0 0       0 unless defined $attr_hr->{"start_line"};
481            
482             #do nothing with C<>, L<> element
483            
484             $self->{log}->debug(
485             "e_name: ",
486             $e_name,
487             " line: ",
488 0 0       0 $attr_hr->{"start_line"},
    0          
489             " token: ",
490             ( $token
491             ? $token->get_key
492             . " key_needed: "
493             . ( $token->key_needed() ? " true" : " false" )
494             : " undef"
495             )
496             );
497            
498 0 0       0 if ( $token->is_pod_matching($e_name) ) {
499            
500             $self->{log}->debug(
501 0         0 "current token: ", $token->get_key,
502             " previous: ", $token->previous->get_key
503             );
504 0         0 $token->line_start( $attr_hr->{"start_line"} );
505            
506             #Do not change a value set by _cut_seen, change only the 0 default value
507 0 0       0 $token->previous->line_end( $attr_hr->{"start_line"} - 1 )
508             if ( $token->previous->line_end() == 0 );
509             }
510            
511 0         0 my $tp = $token->previous;
512            
513 0 0 0     0 if ( $token->key_needed() && $key ) {
514 0         0 $self->{log}->debug( "testing : ", $token->get_key, " / ", $key );
515 0 0       0 if ( $token->is_key_matching($key) ) {
516            
517             #$last_end = $attr_hr->{"start_line"} - 1;
518             #Set the end of the previous token if it's not elready done
519 0 0       0 $tp->line_end( $token->line_start - 1 ) unless ( $tp->line_end );
520             #$token_match = $token;
521 0         0 $self->{log}->debug("moving to the next token");
522 0         0 $token = $token->next;
523            
524             }
525             else {
526 0         0 $self->{log}->debug( $token->get_key, " no match with $key" );
527            
528             #stop fetching text for the key since a match has be done
529 0         0 $token->key_needed(0);
530             }
531 0         0 $key = undef;
532             } #if
533            
534             else {
535             #key not needed or $key undef
536 0 0       0 if ( $e_name =~ /^head/i ) {
537             $self->{log}
538 0         0 ->debug( "token->key_needed is false or key undef. tp current line end : ", $tp->line_end );
539             #close the last token fetched if it's still 0
540 0 0       0 $tp->line_end( $attr_hr->{"start_line"} - 1 )
541             unless ( $tp->line_end );
542            
543             }
544             }
545            
546             }
547            
548             #uo parsing: the compiled regex for the pod directives and the keys
549             #are in the root token for the whole parsing.
550             #The children token are created during the parsing when a key (from the file) matched with a key pattern
551             #The starting line and ending line are
552            
553             sub _handle_element_start_uo {
554 0     0   0 my ( $self, $e_name, $attr_hr ) = @_;
555             return
556 0 0       0 unless defined $attr_hr->{"start_line"};
557 0 0       0 $token = $self->{doc} unless ($token);
558            
559             $self->{log}->debug(
560             "e_name: ",
561             $e_name,
562             " line: ",
563             $attr_hr->{"start_line"},
564             " token: ",
565             ( defined $token ) ? $token->get_key : " token undef",
566             " key_needed: ",
567 0 0       0 ( $self->{doc}->key_needed() ? " true" : " false" )
    0          
568             );
569            
570 0 0       0 if ( $self->{doc}->is_pod_matching($e_name) ) {
571            
572             #put the line of the matching pod directive in the root token
573             #the matching pod directive can be retrieve with $self->{doc}->get_pod
574 0         0 $self->{doc}->line_start( $attr_hr->{"start_line"} );
575 0 0       0 if ( $token->previous ) {
576 0 0       0 $token->previous->line_end( $attr_hr->{"start_line"} - 1 )
577             if ( $token->previous->line_end() == 0 );
578             }
579             else {
580 0         0 $self->{log}->debug("tp undef");
581             }
582            
583             }
584            
585             $self->{log}->debug(
586             " self->{doc}->key_needed: ",
587 0 0       0 ( $self->{doc}->key_needed() ? " true" : " false" ),
    0          
588             " key is ", ( defined $key ? $key : " undef" )
589             );
590            
591 0 0 0     0 if ( $self->{doc}->key_needed() && $key ) {
592            
593 0         0 $self->{log}->debug( "testing : ", $key );
594            
595 0 0       0 if ( $self->{doc}->is_key_matching($key) ) {
596 0         0 $self->{log}->debug("Found match");
597            
598             #$last_end = $attr_hr->{"start_line"} - 1;
599 0 0       0 if ( $token->previous ) {
600 0 0       0 $token->previous->line_end( $attr_hr->{"start_line"} - 1 )
601             unless ( $token->previous->line_end );
602             }
603             else {
604 0         0 $self->{log}->debug("tp undef");
605             }
606 0         0 $self->{log}->debug("next token - new token");
607            
608             my $new_token =
609             Pod::Simple::Select::Token->new( $self->{doc},
610 0         0 $self->{doc}->get_pod );
611            
612 0         0 $new_token->set_key($key);
613            
614             #retrieve back the line_start stored when the pod
615             #dir of the current key was matching
616 0         0 $new_token->line_start( $self->{doc}->line_start );
617            
618             #set the line end the soon to become previous token
619             #unless the line_end already exists
620 0 0       0 $token->line_end( $self->{doc}->line_start - 1 )
621             unless ( $token->line_end );
622 0         0 $new_token->previous($token);
623 0         0 $token = $token->next($new_token);
624             } #if
625             else {
626             $self->{log}->debug(
627             "no match ! pod_index: ",
628             $self->{doc}->{pod_index},
629             " keys ",
630             join(
631             " ",
632 0         0 @{ $self->{doc}->{key_pat}->[ $self->{doc}->{pod_index} ]
  0         0  
633             }
634             )
635             );
636            
637             #set the line end of the current token before the start of
638             #this section having a non matching key
639             #tester =head1 found =head3 Non matching pod
640 0 0       0 $token->line_end( $self->{doc}->line_start - 1 )
641             unless ( $token->line_end );
642             }
643 0         0 $key = undef;
644             }
645             else {
646             #key not needed or $key undef
647 0 0       0 if ( $e_name =~ /^head/i ) {
648 0         0 my $last_end = $attr_hr->{"start_line"} - 1;
649 0 0       0 if ( $token->previous ) {
650 0         0 $self->{log}->debug( "tp current line end : ",
651             $token->previous->line_end );
652            
653 0 0       0 $token->previous->line_end($last_end)
654             unless ( $token->previous->line_end );
655             }
656             else {
657 0         0 $self->{log}->debug("tp undef");
658             }
659            
660             }
661             }
662            
663             }
664            
665            
666             sub _handle_text {
667 0     0   0 my ( $self, $text ) = @_;
668 0 0       0 return unless $token;
669 0 0       0 if ( $token->key_needed() ) {
670 0         0 $key .= $text;
671             }
672 0         0 $self->{log}->debug($text);
673             }
674            
675             sub _handle_text_uo {
676 0     0   0 my ( $self, $text ) = @_;
677 0 0       0 if ( $self->{doc}->key_needed() ) {
678 0         0 $key .= $text;
679             }
680 0         0 $self->{log}->debug($text);
681            
682             }
683            
684            
685             #
686             #Pod::Simple::Select::Token is used in both ordred unordered parsing
687             #It's not used outsite this module so it is not included in separate file
688             #
689             #Ordered parsing : a list of token ordred by next-previous links and parent-child links
690             #parent - child links are made by the args given in select
691             #next-previous is a deep first search in the tree made in $parser->make_tree
692             #{key_pat} holds an array ref of one array ref that hold the compiled regex of a key
693             #{pod_pat} holds an array ref of one array ref that hold .... of the pod directive
694             #{pod_index} is 0 in every token
695             #{pod} is the pod directive received in the token contructor
696             #
697             #Unordered parsing: a list of token is made in _handle_element_start_uo when a pod directive
698             #and key match with the keys and pod patterns stored in the root token (stored in $parser->{doc}
699             #all the token are children of the root, next-previous is a link between all these children token
700             #In the root token
701             #{pod_pat} holds an array ref of arrays ref, each having a compiled pod directives
702             #{key_pat} holds an array ref of arrays ref, each having a compiled regex for a key
703             #when a pod directive from the file matched, the index in the @{$self->{pod_array}}
704             #is stored in {pod_index}. So the corresponding array of compiled keys regex can be used
705             #in @{$self->{key_pat}} to test a match with a key.
706             #The pod directive that matched is remembered in $parser->{doc}->{pod}
707             #
708             package Pod::Simple::Select::Token;
709            
710             #use Carp qw/confess/;
711 1     1   8 use Data::Dumper;
  1         2  
  1         978  
712             my $current_child = 0;
713            
714             sub new {
715 4     4   11 my ( $class, $parent, $pod_dir ) = @_;
716 4         10 my $self->{parent} = $parent;
717 4         8 bless $self, $class;
718            
719 4 100       13 if ($parent) {
720            
721             #index position of this token in the parent token
722 3         10 $self->{index} = $parent->add_child($self);
723 3         5 $self->{level} = $parent->{level} + 1;
724 3         9 $self->{key} = $parent->get_key . "_" . $pod_dir . "_";
725 3         7 $self->{key_needed} = 0;
726             }
727             else {
728 1         7 $self->{level} = 0;
729 1         3 $self->{key_pat} = [];
730 1         4 $self->{key} = $pod_dir;
731             }
732 4         7 $self->{pod_index} = 0;
733 4         9 $self->{line_pos} = [ 0, 0 ];
734 4         12 $self->set_pod_pattern($pod_dir);
735 4         8 $self->{children} = [];
736 4         10 $self->{log} = Log::Log4perl::get_logger(__PACKAGE__);
737 4         531 return $self;
738             }
739            
740             sub set_key {
741 2     2   5 my ( $self, $key ) = @_;
742 2         9 $self->{log}->debug( " key: ", $key );
743 2         18 $self->{key} .= $key;
744 2         4 push @{ $self->{key_pat} }, [qr/\b$key\b/i];
  2         22  
745             }
746            
747             sub get_key {
748 9     9   23 return shift->{key};
749             }
750            
751             sub get_key_pattern {
752 0     0   0 my $self = shift;
753            
754 0         0 return $self->{key_pat};
755            
756             }
757            
758             sub get_pod_pattern {
759 0     0   0 return shift->{pod_pat};
760             }
761            
762             sub set_pod_pattern {
763 4     4   10 my ( $self, $pod_dir ) = @_;
764 4 50       10 if ( ref $pod_dir eq "ARRAY" ) {
765 0         0 my @pat;
766 0         0 for my $pod (@$pod_dir) {
767 0         0 push @pat, qr/\b$pod\b/i;
768             }
769 0         0 $self->{pod_pat} = \@pat;
770             }
771             else {
772 4         51 $self->{pod_pat} = [qr/\b$pod_dir\b/i];
773 4         12 $self->{pod} = $pod_dir
774             ; #ordered args: holds the head1 etc given in the select call
775             #unordered args : holds the pod_directive last match
776             }
777             }
778            
779             sub add_child {
780 3     3   13 my ( $self, $child ) = @_;
781             $self->{first_child} = $child
782 3 100       6 unless ( scalar @{ $self->{children} } );
  3         9  
783 3         5 push @{ $self->{children} }, $child;
  3         6  
784 3         4 return @{ $self->{children} } - 1;
  3         8  
785            
786             #next est le premier ne s'il y a des descendants
787             #sinon c est le voisin de meme niveau
788             }
789            
790             sub previous {
791 3     3   6 my ( $self, $v ) = @_;
792 3 50       10 if ($v) {
793 3         5 $self->{previous} = $v;
794             }
795 3 50       8 return $self->{previous} if ( $self->{previous} );
796             }
797            
798             sub next {
799 4     4   9 my ( $self, $v ) = @_;
800 4 100       10 if ($v) {
801 3         5 $self->{next} = $v;
802             }
803            
804             =for comment
805             if ($self->{first_child} && ! $seen{ $self->{key} }) {
806             $seen{ $self->{key}}= 1;
807             return $self->{first_child};
808             }
809             return $self->{parent}->child_at($self->{index}+1) if ( $self->{parent});
810             # return $self->{parent}->next if ($self->{parent});
811             =cut
812            
813 4 50       11 return $self->{next} if ( $self->{next} );
814             }
815             #
816             #Return the token at pos $index if $index is in the range of 0 -> last_child (: children_count -1)
817             #Else if there is a parent, return the next sibling
818             #(the token next to the current token in the parent children list
819             #
820             sub child_at {
821 4     4   11 my ( $self, $index ) = @_;
822 4 50       10 $self->{log}->logconfess("called on an empty list")
823             unless $self->children_count;
824             $self->{log}
825 4         9 ->debug( "searching ", $index, " in 0-", @{ $self->{children} } - 1,
  4         12  
826             " indexes" );
827 4 100       27 if ( $index < @{ $self->{children} } ) {
  4         9  
828 3         7 return $self->{children}->[$index];
829             }
830             else {
831             return $self->{parent}->child_at( $self->{index} + 1 )
832 1 50       5 if ( $self->{parent} );
833             }
834            
835             }
836            
837             sub children {
838 3     3   4 return @{ shift->{children} };
  3         7  
839             }
840            
841             sub line_start {
842 0     0   0 my ( $self, $pos ) = @_;
843 0 0       0 if ($pos) {
844 0         0 $self->{line_pos}->[0] = $pos;
845             $self->{log}
846 0         0 ->debug( "line_start: ", $self->get_key, " set at ", $pos );
847             }
848 0         0 return $self->{line_pos}->[0];
849             }
850            
851             sub line_end {
852 0     0   0 my ( $self, $pos ) = @_;
853 0 0       0 if ($pos) {
854 0         0 $self->{line_pos}->[1] = $pos;
855 0         0 $self->{log}->debug( $self->get_key, " set at ", $pos );
856            
857             }
858 0         0 return $self->{line_pos}->[1];
859             }
860            
861             sub next_index {
862 3     3   8 return shift->{index} + 1;
863             }
864            
865             sub children_count {
866 7     7   11 my $self = shift;
867 7         10 my $res = @{ $self->{children} };
  7         10  
868 7         16 return $res;
869            
870             }
871            
872             sub get_level {
873 0     0     return shift->{level};
874             }
875            
876             sub is_pod_matching {
877 0     0     my ( $self, $el ) = @_;
878 0 0         $self->{log}->logconfess("can't treat undef") unless ($el);
879            
880 0 0         if ( $self->{pod_pat} ) {
881 0           my $i = 0;
882 0           for my $p ( @{ $self->{pod_pat} } ) {
  0            
883 0           $i++;
884 0 0         if ( $el =~ /\b$p\b/ ) {
885 0           $self->{key_needed} = 1;
886 0           $self->{pod_index} = $i - 1;
887 0           $self->{pod} = $el;
888             $self->{log}->debug( "Found pod match for ",
889             $self->get_key, " and ", $el, " with index ",
890 0           $self->{pod_index} );
891 0           return 1;
892             }
893             }
894             }
895 0           return 0;
896             }
897            
898             sub is_key_matching {
899 0     0     my ( $self, $el ) = @_;
900 0 0         $self->{log}->logconfess("can't treat undef") unless ($el);
901 0           my $key_r = $self->{key_pat}->[ $self->{pod_index} ];
902 0           $self->{log}->debug("is_key_matching index:", $self->{pod_index});
903 0           $self->{key_needed} = 0;
904 0           $self->{log}->debug("key_needed set to 0");
905 0           for my $p (@$key_r) {
906 0           $self->{log}->debug("searching $el pattern: $p");
907 0 0         if ( $el =~ /\b$p\b/ ) {
908 0 0         $self->set_key($el) unless $self->{key};
909 0           $self->{log}->debug( "Found key match for ",
910             $self->get_key, " and ", $el );
911             #$self->{key_needed} = 0;
912 0           return 1;
913             }
914             }
915 0           return 0;
916            
917             }
918            
919             sub get_pod {
920 0     0     return shift->{pod};
921             }
922            
923             sub key_needed {
924 0     0     my ( $self, $v ) = @_;
925 0 0         $self->{key_needed} = $v if ( defined $v );
926 0           return $self->{key_needed};
927             }
928            
929             sub _print_patt {
930 0     0     my $self = shift;
931 0           print "print_patt ", Dumper( $self->{pod_pat} ), "\n";
932 0 0         if ( ref $self->{key_pat} ) {
933 0           for my $k ( @{ $self->{key_pat} } ) {
  0            
934 0           print "key_pat ", Dumper(@$k), "\n";
935             }
936             }
937             else {
938 0           print "key_pat ", $self->{key_pat}, "\n";
939             }
940             }
941            
942             1;
943            
944             =head1 BUGS
945            
946             See support below.
947            
948             =head1 SUPPORT
949            
950             Any questions or problems can be posted to me (rappazf) on my gmail account.
951            
952             =head1 AUTHOR
953            
954             FranEois Rappaz
955             CPAN ID: RAPPAZF
956            
957             =head1 COPYRIGHT
958            
959             FranEois Rappaz 2017
960             This program is free software; you can redistribute
961             it and/or modify it under the same terms as Perl itself.
962            
963             The full text of the license can be found in the
964             LICENSE file included with this module.
965            
966            
967             =head1 SEE ALSO
968            
969             L
970            
971             L
972            
973             =cut
974