Icon for Dimension.orgIcon for TalentIcon for ToolsIcon for Doorways.org
Icon for ToolsIcon for Perl Tools

DoxyFilt: POD/Filter/Filter.pm Source File

  • Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List

POD/Filter/Filter.pm

Go to the documentation of this file.
00001 package Doxygen::POD::Filter;
00002 
00003 =head1 NAME
00004 
00005 Doxygen::POD::Filter - Perl extension for generating Doxygen documentation
00006 
00007 =head1 SYNOPSIS
00008 
00009     my  $filter = new Doxygen::POD::Filter($path);
00010 
00011 =head1 ABSTRACT
00012 
00013 The basic POD filter, or parser.
00014 Processes a file of data looking for POD statements.
00015 Stores the data on itself (single use only)
00016 and generates Doxygen-parseable output as required.
00017 
00018 =head1 DESCRIPTION
00019 
00020 =head1 METHODS
00021 
00022 =over
00023 
00024 =cut
00025 
00026 use     5.005;  # just to pick something, but not really tested
00027 use     strict;
00028 use     warnings;
00029 use     UNIVERSAL   qw(can isa);
00030 
00031 use     Carp;
00032 
00033 use     Doxygen::POD::Item;
00034 use     Doxygen::POD::Item::Code;
00035 use     Doxygen::POD::Item::Format;
00036 use     Doxygen::POD::Item::Head;
00037 use     Doxygen::POD::Item::Over;
00038 
00039 use     base qw(Doxygen::Filter);
00040 
00041 our $VERSION = '0.01';
00042 
00043 use     constant    TYPE_FLAG   =>  (
00044     skipHead    =>  qr(^(?:METHOD|FUNCTION)S?$)
00045 );
00046 
00047 ###########################################################################
00048 
00049 =item   C<CleanText(\$text)>
00050 
00051 Remove all POD from the text referenced.
00052 
00053 Removes the number of blocks removed.
00054 
00055 =cut
00056 
00057 sub CleanText   # $text
00058 {
00059    ${$_[0]} =~ s/(?:\n|^)=.*?\n=cut.*?(?=\n|$)//gs
00060 }
00061 
00062 ###########################################################################
00063 ###########################################################################
00064 
00065 =item   C<parse($text, $source)>
00066 
00067 Parse a source file.
00068 
00069 Attach results (Doxygen::Item objects) to Doxygen::Source object.
00070 
00071 =begin doxygen
00072 @note This is a line-by-line state machine.
00073 Works OK just on the POD,
00074 since now the Perl parsing has been removed to another file.
00075 Was really complicated before when they were combined into
00076 one parser.
00077 =end
00078 
00079 =cut
00080 
00081 sub parse       # $self, $text, $source
00082 {
00083     my  $self = shift;
00084     my ($text, $source) = @_;
00085     
00086     my  @focus = ( $self->{root} = new Doxygen::POD::Item );
00087     my  $atEND = 0;
00088     my  $depth = 0;
00089     my  $inPOD = 0;
00090     
00091     $self->{lines} = {
00092         Perl    =>  0,
00093         total   =>  0
00094     };
00095     
00096     foreach (split /\n/, $text) {
00097         $self->{lines}->{total}++;
00098         
00099 #       $source->log('T', 'Line ', $self->{lines}->{total}, ' ', scalar(@focus));
00100         
00101         unless (defined) {
00102             $self->{lines}->{Perl}++ unless $inPOD;
00103             next;
00104         }
00105         
00106         if (isa($focus[0], 'Doxygen::POD::Item::Format')) {
00107             # Currently in formatting mode:
00108             if (/^=end/) {
00109                 shift @focus;
00110                 next;
00111             }
00112             
00113             if ($atEND && /^\@page\b/) {
00114                 # If we find an @page beyond __END__
00115                 #   reset the focus to the current file:
00116                 my  $format = shift @focus;
00117                 
00118                 $focus[0]->textPop('comment');
00119                 @focus = ( $format );
00120                 $source->getFile->textAppend('pages', $format);
00121             }
00122             
00123             $focus[0]->textAppend('comment', "$_\n");
00124             next;
00125         }
00126         
00127         next
00128             if /^=pod\b/;
00129         
00130         my  $line = \$_;
00131         
00132         if (/^=cut\b/) {
00133             shift @focus
00134                 if isa $focus[0], 'Doxygen::POD::Item::Code';
00135             
00136             $inPOD = 0;
00137             next;
00138         } elsif (/^=head([1234])\s*(.*?)\s*$/) {
00139             # Documentation header level:
00140             my ($level, $name) = (int($1), $2);
00141             
00142             shift @focus
00143                 if isa $focus[0], 'Doxygen::POD::Item::Code';
00144             
00145             # Need to pop headers that are less than or equal
00146             #   to the level of this new header:
00147             my  $popTo = undef;
00148             
00149             for (my $i = 0; $i < @focus; $i++) {
00150                 next unless isa $focus[$i], 'Doxygen::POD::Item::Head';
00151                 $popTo = $i+1 if $focus[$i]->{level} >= $level;
00152                 last if $focus[$i]->{level} <= $level;
00153             }
00154             
00155             splice @focus, 0, $popTo if defined $popTo;
00156             
00157             my  $item = new Doxygen::POD::Item::Head
00158                                 (level => $level,
00159                                  name  => $name);
00160             
00161             if (isa $item, 'Doxygen::POD::Item::Head') {
00162                 $focus[0]->textAppend('comment', $item);
00163                 unshift @focus, $item;
00164                 push @{$self->{heads}}, $item if $level == 1;
00165             } else {
00166                 $source->log('W', 'Unable to create head ', $level, ' ', $name);
00167             }
00168             
00169             $inPOD = 1;
00170             next;
00171         } elsif (/^=over\b/) {
00172 #           $source->log('T', '=over at line ', $self->{lines}->{total});
00173             
00174             # Move in one list/indentation level:
00175             shift @focus
00176                 if isa $focus[0], 'Doxygen::POD::Item::Code';
00177             
00178             my  $item = new Doxygen::POD::Item::Over;
00179             
00180             if (isa $item, 'Doxygen::POD::Item::Over') {
00181                 $focus[0]->textAppend('comment', $item);
00182                 unshift @focus, $item;
00183             } else {
00184                 $source->log('W', 'Unable to create indentation (over) object');
00185             }
00186             
00187             $inPOD = 1;
00188             $depth++;
00189             next;
00190         } elsif (/^=back\b/) {
00191 #           $source->log('T', '=back at line ', $self->{lines}->{total});
00192             
00193             # Back out one list/indentation level:
00194             shift @focus
00195                 while $focus[0]
00196                    &&   isa($focus[0], 'Doxygen::POD::Item')
00197                    && ! isa($focus[0], 'Doxygen::POD::Item::Over')
00198                    && $focus[0] != $self->{root};
00199             
00200             if (isa($focus[0], 'Doxygen::POD::Item::Over')) {
00201                 shift @focus;
00202                 $depth--;
00203             } else {
00204                 $source->log('W', "No =over to match =back\nFocus:  ",
00205                              $focus[0], ' of ', scalar(@focus), "\nRoot:   ",
00206                              $self->{root});
00207             }
00208             $inPOD = 1;
00209             next;
00210         } elsif (/^=item\s+(.*?)\s*$/) {
00211             # A list item:
00212             my  $name = $1;
00213             
00214             shift @focus
00215                 if isa $focus[0], 'Doxygen::POD::Item::Code';
00216             
00217             shift @focus
00218                 while $focus[0]
00219                    && ref($focus[0]) eq 'Doxygen::POD::Item'
00220                    && $focus[0] != $self->{root};
00221             
00222             my  $item = new Doxygen::POD::Item(name => $name);
00223             
00224             if (isa $item, 'Doxygen::POD::Item') {
00225                 $focus[0]->textAppend('comment', $item);
00226                 unshift @focus, $item;
00227             } else {
00228                 $source->log('W', 'Unable to create item ', $name);
00229             }
00230                    
00231             push @{$self->{items}}, $focus[0]
00232                 if $depth == 1
00233                 && $name !~ /^[*+=-]$/
00234                 && $name !~ /^[A-Z]$/
00235                 && $name !~ /^-?\d+(?:\.\d*)?$/;
00236             
00237             $inPOD = 1;
00238             next;
00239         } elsif (/^=(for|begin)\s+(\w+)(?:\s+(.*?))?\s*$/) {
00240             # Go into special 'format' mode:
00241             my ($cmd, $fmt, $stuff) = ($1, $2, $3);
00242             
00243             shift @focus
00244                 if isa $focus[0], 'Doxygen::POD::Item::Code';
00245             
00246             my  $format = new Doxygen::POD::Item::Format(_fmt_ => $fmt);
00247             
00248             unless (isa($format, 'Doxygen::POD::Item::Format')) {
00249                 $source->log('W', 'Unable to create format object');
00250             } elsif ($cmd eq 'for') {
00251                 # Special single-line mode:
00252                 $focus[0]->textAppend('comment', $format);
00253                 $format->textAppend('text', "$stuff\n");
00254             } else {
00255                 # Normal multi-line mode:
00256                 $focus[0]->textAppend('comment', $format);
00257                 unshift @focus, $format;
00258             }
00259             
00260             $inPOD = 1;
00261             next;
00262         } elsif (/^=[a-zA-Z]/) {
00263             $source->log('W', "Unknown POD directive:\n", $_);
00264             $inPOD = 1;
00265             next;
00266         } elsif (! $inPOD) {
00267             # Tripping through the Perl,
00268             #   don't fall through to text copy:
00269             $self->{lines}->{Perl}++;
00270             
00271             $atEND = 1
00272                 if /^__END__\s*$/;
00273             
00274             next;
00275         } elsif (/^Doxygen(?:::\w+)+\s*-\s*(.*?)\s*$/) {
00276             # TODO WTF???
00277             # Attempt to match common first-line practice:
00278             my  $brief = $1;    $line = \$brief;
00279         } elsif (/^\s+\S/) {
00280             # Indented CODE object:
00281             unless (isa $focus[0], 'Doxygen::POD::Item::Code') {
00282                 my  $item = new Doxygen::POD::Item::Code;
00283 
00284                 if (isa $item, 'Doxygen::POD::Item::Code') {
00285                     $focus[0]->textAppend('comment', $item);
00286                     unshift @focus, $item;
00287                 } else {
00288                     $source->log('W', 'Unable to create code block');
00289                 }
00290             }
00291         } elsif (/^\S/) {
00292             # Just some POD code:
00293             shift @focus
00294                 if isa $focus[0], 'Doxygen::POD::Item::Code';
00295         }
00296         
00297         # For all the cases above that still needed to append
00298         #   some stuff to the comments collecting for the focus:
00299         
00300         $focus[0]->textAppend('comment', "$$line\n");
00301     }
00302     
00303     # Somewhat clumsy, but easier than really keeping track:
00304     $self->{lines}->{POD} =
00305         $self->{lines}->{total} - $self->{lines}->{Perl};
00306 }
00307 
00308 ###########################################################################
00309 ###########################################################################
00310 
00311 =item   C<massage($self, $source)>
00312 
00313 Massage items in file after parsing and before generating.
00314 
00315 Each filter may have a C<massage> method and/or a C<parse> method.
00316 The order of parsing and massaging is undefined except that
00317 I<all> parsing will complete prior to I<any> massaging being done.
00318 
00319 This overload of C<massage> is used to connect POD items on this object to
00320 applicable Doxygen items on the source object, thereby enriching the latter.
00321 
00322 Basically it looks for items named the same as functions that have
00323 been defined during the parse.
00324 If a match is found, the comment lines from the item are copied to
00325 the comment lines on the function and the item is changed to show
00326 a link to the function itself.
00327 
00328 =begin doxygen
00329 
00330 @note
00331 
00332 This code was rewritten to use the names of functions
00333 as the search space and match items.
00334 This way if there are no functions,
00335 because the source is a POD file and has no actual code in it,
00336 the search will end quickly and not result in accidental matchups.
00337 
00338 We originally tried to move much of this method to Doxygen::Perl::Filter,
00339 but it just did not know where the POD was, and rightfully so.
00340 Whereas from here the functions,
00341 which are of class Doxygen::Item::Function
00342 and thus neither POD-specific nor Perl-specific,
00343 are easily accessible.
00344 
00345 =end
00346 
00347 =cut
00348 
00349 sub massage     # $self, $source
00350 {
00351     my ($self, $source) = @_;
00352     
00353 #   $source->log('T', __PACKAGE__, ':"massage(', $source, ')')->pushLog;
00354     
00355     my  $root  = $self->{root};
00356     
00357     # will this do anything if $root is a Doxygen::Item?
00358     $root->massage($source);
00359     
00360     my  $file  = $source->getFile;
00361     my  $focus = $file->focus;
00362     
00363     $focus->textAppend('comment', @{$root->text('comment')})
00364         if isa $root->text('comment'), 'ARRAY';
00365         
00366     $focus->textAppend('notes', @{$root->text('notes')})
00367         if isa $root->text('notes'), 'ARRAY';
00368     
00369     my  $skip = $source->flag('skipHead', $self);
00370     
00371     for my $head (@{$self->{heads}}) {
00372         if ($head->{name} eq 'NAME') {
00373             my $cmnt = $head->text('comment');
00374             
00375             if (isa($cmnt, 'ARRAY')) {
00376                 for (@$cmnt) {
00377                     if (/^\s*<tt>.*?<\/tt>\s*(?:-\s*)?(.*)$/) {
00378                         $focus->textAppend('brief', $1)
00379                             if $1;
00380                     } else {
00381                         $focus->textAppend('brief', $_);
00382                     }
00383                 }
00384             }
00385             
00386             $head->{disabled} = 1;
00387         } elsif (isa($skip, 'Regexp') && $head->{name} =~ $skip) {
00388             $head->{disabled} = 1;
00389         } elsif ($head->{name} =~ /^NOTES?$/) {
00390             $focus->textAppend('notes', "\@note\n", $head->text('comment'));
00391             $head->{disabled} = 1;
00392         } elsif ($head->{name} =~ /^AUTHORS?$/) {
00393             for my $item (@{$head->text('comment')}) {
00394                 $focus->textAppend('notes', "\@author $item")
00395                     if $item
00396                     && $item =~ /\S/;
00397             }
00398             $head->{disabled} = 1;
00399         }
00400     }
00401     
00402     my  @funcs = $file->items('function');
00403     
00404     unless ($file == $focus) {
00405       focusFunc:
00406         for my $func ($focus->items('function')) {
00407             ($_ == $func && next focusFunc) for @funcs;
00408             push @funcs, $func;
00409         }
00410     }
00411     
00412     my  %items = ( );
00413     my  %args  = ( );
00414     
00415     for my $item (@{$self->{items}}) {
00416         next
00417             unless $item->{name}
00418                 && $item->{name} =~ /(\w+)\s*\((.*?)\)/;
00419 
00420         $args {$1} = $2;
00421         $items{$1} = $item;
00422     }
00423         
00424     for my $func (@funcs) {
00425         unless (isa($func, 'Doxygen::Item::Function')) {
00426             $source->log('W', "Function item not a function:\n  ", $func);
00427             next;
00428         }
00429     
00430         my  $name = $func->{name};
00431         
00432         unless ($name) {
00433             $source->log('W', 'Function without name');
00434             next;
00435         }
00436         
00437         my  $item = $items{$name};
00438         
00439         unless ($item) {
00440             $source->log('W', 'No item for function ', $name)
00441                 unless $name =~ /^[A-Z]+$/
00442                     || $name =~ /^_/;
00443             
00444             next;
00445         }
00446         
00447         unless (isa($item, 'Doxygen::Item')) {
00448             $source->log('W', 'Item for function ', $name,
00449                               ' not a Doxygen::Item');
00450             next;
00451         }
00452         
00453         # Copy arguments to function object:
00454         #=| @todo   when copying arguments from item to function,
00455         #=|         compare them and generate warnings on mismatch
00456         $func->arguments($args{$name});
00457         
00458         # Copy comments to function object:
00459         $func->textAppend('comment', @{$item->text('comment')})
00460             if isa $item->text('comment'), 'ARRAY';
00461         
00462         $func->textAppend('notes',   @{$item->text('notes')})
00463             if isa $item->text('notes'), 'ARRAY';
00464             
00465 #       # Instead of disabling item,
00466 #       #   replace its comments and make it a bullet item
00467 #       #   (usually the containing METHODS or FUNCTIONS =head1
00468 #       #    will have been removed anyway):
00469 #       $item->textClear ('comment');
00470 #       $item->textAppend('comment',
00471 #                         $func->image(linked => 1, escaped => 1));
00472 #       $item->{name} = '*';
00473         
00474         # Disable the item,
00475         #   Doxygen provides a TOC to functions so we
00476         #   don't need to do our own:
00477         $item->{disabled} = 1
00478     }
00479     
00480     $source->popLog;
00481 }
00482 
00483 ###########################################################################
00484 ###########################################################################
00485 
00486 =item   C<flags($self, $name)>
00487 
00488 Return reference to flags hash for filter.
00489 
00490 These are default values.
00491 
00492 =cut
00493 
00494 sub flags
00495 {
00496     my ($self, $name) = @_;
00497     
00498     unless ($self->{_POD_flags_}) {
00499         my  %flags = TYPE_FLAG;
00500         
00501         $self->{_POD_flags_} = \%flags;
00502     }
00503     
00504     $self->{_POD_flags_}
00505 }
00506 
00507 ###########################################################################
00508 ###########################################################################
00509 
00510 1
00511 
00512 __END__
00513 
00514 =back
00515 
00516 =head1 SEE ALSO
00517 
00518 DoxyFilt.pl Doxygen::Filter
00519 
00520 =head1 AUTHOR
00521 
00522 Marc M. Adkins, L<mailTo:Perl@Doorways.org>
00523 
00524 =head1 COPYRIGHT AND LICENSE
00525 
00526 Copyright 2004-2010 by Marc M. Adkins
00527 
00528 This library is free software; you can redistribute it and/or modify
00529 it under the same terms as Perl itself.
00530 
00531 =cut

Generated on Mon Dec 27 2010 15:15:39 for DoxyFilt by  doxygen 1.7.1

www.dimension.org logo

(C)opyright 1998 - 2012 Dimension.org

WebMaster