home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / POD.pm < prev    next >
Encoding:
Perl POD Document  |  2002-08-19  |  9.3 KB  |  378 lines

  1. package DocSet::Source::POD;
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. use DocSet::Util;
  7. use DocSet::RunTime;
  8.  
  9. use vars qw(@ISA);
  10. require DocSet::Doc;
  11. @ISA = qw(DocSet::Doc);
  12.  
  13. use constant HEAD_MAX_LEVEL => 4;
  14. use constant MAX_DESC_LENGTH => 500;
  15.  
  16. # META: we are presenting too early, or this code should be moved to
  17. # POD2HTML specific module
  18. require Pod::POM::View::HTML;
  19. my $mode = 'Pod::POM::View::HTML';
  20.  
  21. sub retrieve_meta_data {
  22.     my($self) = @_;
  23.  
  24.     $self->parse_pod;
  25.  
  26.     #print Pod::POM::View::HTML->print($pom);
  27.  
  28.     my $meta = {
  29.         title => 'No Title',
  30.         abstract => '',
  31.     };
  32.  
  33.     my $pom = $self->{parsed_tree};
  34.     my @sections = $pom->head1();
  35.  
  36.     
  37.     if (@sections) {
  38.  
  39.         # extract the title from the NAME section and remove it from content
  40.         if ($sections[0]->title =~ /NAME/) {
  41.             # don't present on purpose ->present($mode); there should
  42.             # be no markup in NAME a problem with
  43.             # <TITLE><CODE>....</CODE><TITLE> and alike
  44.             $meta->{title} = (shift @sections)->content();
  45.             $meta->{title} =~ s/^\s*|\s*$//sg;
  46.         }
  47.  
  48.         # stitle is the same in docs
  49.         $meta->{stitle} = $meta->{title};
  50.  
  51.         # locate the DESCRIPTION section (should be in the first three
  52.         # sections)
  53.         for (0..2) {
  54.             next unless defined $sections[$_]
  55.                 && $sections[$_]->title =~ /DESCRIPTION/i;
  56.  
  57.             my $abstract = $sections[$_]->content->present($mode);
  58.  
  59. # cannot do this now, as it might cut some markup in the middle: <i>1 2</i>
  60. #            # we are interested only in the first paragraph, or if its
  61. #            # too big first MAX_DESC_LENGTH chars.
  62. #            my $index = index $abstract, " ", MAX_DESC_LENGTH;
  63. #            # cut only if index didn't return '-1' which is when the the
  64. #            # space wasn't found starting from location MAX_DESC_LENGTH
  65. #            unless ($index == -1) {
  66. #                $abstract = substr $abstract, 0, $index+1;
  67. #                $abstract .= " ... <i>(continued)</i>";
  68. #            }
  69. #
  70. #           # temp workaround, but can only split on paras
  71.             $abstract =~ s|<p>(.*?)</p>.*|$1|s;
  72.  
  73.             $meta->{abstract} = $abstract;
  74.             last;
  75.         }
  76.     }
  77.  
  78.     $meta->{link} = $self->{rel_dst_path};
  79.  
  80.     # put all the meta data under the same attribute
  81.     $self->{meta} = $meta;
  82.  
  83.     # build the toc datastructure
  84.     my @toc = ();
  85.     my $level = 1;
  86.     for my $node (@sections) {
  87.         push @toc, $self->render_toc_level($node, $level);
  88.     }
  89.     $self->{toc} = \@toc;
  90.  
  91. }
  92.  
  93. sub render_toc_level {
  94.     my($self, $node, $level) = @_;
  95.     my $title = $node->title;
  96.     my $link = "$title";     # must stringify to get the raw string
  97.     $link =~ s/^\s*|\s*$//g; # strip leading and closing spaces
  98.     $link =~ s/\W/_/g;       # META: put into a sub? see Doc::Common::pod_pom_html_anchor
  99.     # prepand '#' for internal links
  100.     my $toc_link = "toc_$link"; # self referring toc entry
  101.     $link = "#$link";
  102.  
  103.     my %toc_entry = (
  104.         title    => $title->present($mode), # run the formatting if any
  105.         link     => $link,
  106.         toc_link => $toc_link,
  107.     );
  108.  
  109.     my @sub = ();
  110.     $level++;
  111.     if ($level <= HEAD_MAX_LEVEL) {
  112.         # if there are deeper than =head4 levels we don't go down (spec is 1-4)
  113.         my $method = "head$level";
  114.         for my $sub_node ($node->$method()) {
  115.             push @sub, $self->render_toc_level($sub_node, $level);
  116.         }
  117.     }
  118.     $toc_entry{subs} = \@sub if @sub;
  119.  
  120.     return \%toc_entry;
  121. }
  122.  
  123.  
  124.  
  125. sub parse_pod {
  126.     my($self) = @_;
  127.     
  128.     # already parsed
  129.     return if exists $self->{parsed_tree} && $self->{parsed_tree};
  130.  
  131. #    print ${ $self->{content} };
  132.  
  133.     use Pod::POM;
  134.     my %options;
  135.     my $parser = Pod::POM->new(\%options);
  136.     my $pom = $parser->parse_text(${ $self->{content} })
  137.         or die $parser->error();
  138.  
  139.     $self->{parsed_tree} = $pom;
  140.  
  141.     # examine any warnings raised
  142.     if (my @warnings = $parser->warnings()) {
  143.         print "\n", '-' x 40, "\n";
  144.         print "File: $self->{src_path}\n";
  145.         warn "$_\n" for @warnings;
  146.     }
  147. }
  148.  
  149. sub src_filter {
  150.     my ($self) = @_;
  151.  
  152.     $self->extract_pod;
  153.  
  154.     $self->head2page_breaks() if $self->{docset}->options('slides_mode');
  155.  
  156.     $self->podify_items() if $self->{docset}->options('podify_items');
  157. }
  158.  
  159. sub extract_pod {
  160.     my($self) = @_;
  161.  
  162.     my @pod = ();
  163.     my $in_pod = 0;
  164.     for (split /\n{2,}/, ${ $self->{content} }) {
  165.         unless ($in_pod) {
  166.             s/^[\s\n]*//ms; # skip empty lines in preamble
  167.             $in_pod = /^=/s;
  168.         }
  169.         next unless $in_pod;
  170.         $in_pod = 0 if /^=cut/;
  171.         push @pod, $_;
  172.     }
  173.  
  174.     # handle empty files
  175.     unless (@pod) {
  176.         push @pod, "=head1 NAME", "=head1 Not documented", "=cut";
  177.     }
  178.  
  179.     my $content = join "\n\n", @pod;
  180.     $self->{content} = \$content;
  181. }
  182.  
  183. sub podify_items {
  184.     my($self) = @_;
  185.   
  186.     # tmp storage
  187.     my @paras = ();
  188.     my $items = 0;
  189.     my $second = 0;
  190.  
  191.     # we want the source in paragraphs
  192.     my @content = split /\n\n/, ${ $self->{content} };
  193.  
  194.     foreach (@content) {
  195.         # is it an item?
  196.         if (/^(\*|\d+)\s+((\*|\d+)\s+)?/) {
  197.             $items++;
  198.             if ($2) {
  199.                 $second++;
  200.                 s/^(\*|\d+)\s+//; # strip the first level shortcut
  201.                 s/^(\*|\d+)\s+/=item $1\n\n/; # do the second
  202.                 s/^/=over 4\n\n/ if $second == 1; # start 2nd level
  203.             } else {
  204.                 # first time insert the =over pod tag
  205.                 s/^(\*|\d+)\s+/=item $1\n\n/; # start 1st level
  206.                 s/^/=over 4\n\n/ if $items == 1;
  207.                 s/^/=back\n\n/   if $second; # complete 2nd level
  208.                 $second = 0; # end 2nd level section
  209.             }
  210.             push @paras, split /\n\n/, $_;
  211.         } else {
  212.           # complete the =over =item =back tag
  213.             $second=0, push @paras, "=back" if $second; # if 2nd level is not closed
  214.             push @paras, "=back" if $items;
  215.             push @paras, $_;
  216.           # not a tag item
  217.             $items = 0;
  218.         }
  219.     }
  220.  
  221.     my $content = join "\n\n", @paras;
  222.     $self->{content} = \$content;
  223.  
  224. }
  225.  
  226.  
  227. # add a page break for =headX in slides mode
  228. sub head2page_breaks {
  229.     my($self) = @_;
  230.   
  231.     # we want the source in paragraphs
  232.     my @content = split /\n\n/, ${ $self->{content} };
  233.  
  234.     my $count = 0;
  235.     my @paras = ();
  236.     foreach (@content) {
  237.         # add a page break starting from the third head (since the
  238.         # first is removed anyway, and we don't want to start a new
  239.         # page on the very first page)
  240.         if (/^=head/) {
  241.             $count++;
  242.             if ($count > 2) {
  243.                 push @paras, qq{=for html <?page-break>};
  244.             }
  245.         }
  246.         push @paras, $_;
  247.     }
  248.  
  249.     my $content = join "\n\n", @paras;
  250.     $self->{content} = \$content;
  251.  
  252. }
  253.  
  254. 1;
  255. __END__
  256.  
  257. =head1 NAME
  258.  
  259. C<DocSet::Source::POD> - A class for parsing input document in the POD format
  260.  
  261. =head1 SYNOPSIS
  262.  
  263.  
  264.  
  265. =head1 DESCRIPTION
  266.  
  267. META: not sure if the customized implementation of L<> belongs
  268. here. But it works as follows:
  269.  
  270. Assuming that the main I<config.cfg> specifies the following argument:
  271.  
  272.      dir => {
  273.              ...
  274.   
  275.              # search path for pods, etc. must put more specific paths first!
  276.              search_paths => [qw(
  277.                  docs/2.0/api/mod_perl-2.0 
  278.                  docs/2.0/api/ModPerl-Registry 
  279.                  docs/2.0 
  280.                  docs/1.0
  281.                  .
  282.              )],
  283.   
  284.              # what extensions to search for
  285.              search_exts => [qw(pod pm html)],
  286.   
  287.          },    
  288.  
  289. Whenever the pod includes L<Title|foo::bar/section>, the code will
  290. first convert C<foo::bar> into I<foo/bar> and then will try to find
  291. the file I<foo/bar.pod> in the search path (similar to C<@INC>), as
  292. well as files I<foo/bar.pm> and I<foo/bar.html> under dir I<src>. If
  293. other C<search_exts> are specified they will be searched as well. If
  294. there is a much the link will be created, otherwise only the title of
  295. the link will be displayed.
  296.  
  297. Notice that the C<search_paths> must specify more specific paths
  298. first. If you don't they won't be searched. Currently this is done
  299. only to optimize memory usage and some speed, not sure if that's very
  300. important. But this is different from how Perl does search with
  301. C<@INC> since DocSet reads all the files in memory once and then
  302. reuses this data.
  303.  
  304. =head2 METHODS
  305.  
  306. =over 
  307.  
  308. =item retrieve_meta_data()
  309.  
  310. =item parse_pod()
  311.  
  312. =item podify_items()
  313.  
  314.   podify_items();
  315.  
  316. Podify text to represent items in pod, e.g:
  317.  
  318.   1 Some text from item Item1
  319.   
  320.   2 Some text from item Item2
  321.  
  322. becomes:
  323.  
  324.   =over 4
  325.  
  326.   =item 1
  327.  
  328.   Some text from item Item1
  329.  
  330.   =item 2
  331.  
  332.   Some text from item Item2
  333.  
  334.   =back
  335.  
  336. podify_items() accepts 'C<*>' and digits as bullets
  337.  
  338. podify_items() receives a ref to array of paragraphs as a parameter
  339. and modifies it. Nothing returned.
  340.  
  341. Moreover, you can use a second level of indentation. So you can have
  342.  
  343.   * title
  344.  
  345.   * * item
  346.  
  347.   * * item
  348.  
  349. or 
  350.  
  351.   * title
  352.  
  353.   * 1 item
  354.  
  355.   * 2 item
  356.  
  357. where the second mark is which tells whether to use a ball bullet or a
  358. numbered item.
  359.  
  360. =item head2page_breaks
  361.  
  362. in the I<slides_mode> we want each =headX to start a new slide, so
  363. this mode inserts the page-breaks:
  364.  
  365.   =for html <?page-break>
  366.  
  367. starting from the second header (well actually from the third in the
  368. raw POD, because the first one (NAME) gets stripped before it's seen
  369. by the rendering engine.
  370.  
  371. =back
  372.  
  373. =head1 AUTHORS
  374.  
  375. Stas Bekman E<lt>stas (at) stason.orgE<gt>
  376.  
  377. =cut
  378.