Pod-ToDocBook

 view release on metacpan or  search on metacpan

lib/Pod/ToDocBook/Pod2xml.pm  view on Meta::CPAN

    $type = 'man' if ( $ltext =~ /\(\S*\)/ );
    return {
        type    => $type,
        text    => $ltext,
        linkto  => $linkto,
        base_id => $base_id,
        section => $section
    };
}

# Parse the name and section portion of a link into a name and section.
sub _parse_section {
    my ($link) = @_;
    $link =~ s/^\s+//;
    $link =~ s/\s+$//;

    # If the whole link is enclosed in quotes, interpret it all as a section
    # even if it contains a slash.
    return ( undef, $1 ) if ( $link =~ /^"\s*(.*?)\s*"$/ );

    # Split into page and section on slash, and then clean up quoting in the
    # section.  If there is no section and the name contains spaces, also
    # guess that it's an old section link.
    my ( $page, $section ) = split( /\s*\/\s*/, $link, 2 );
    $section =~ s/^"\s*(.*?)\s*"$/$1/ if $section;
    if ( $page && $page =~ / / && !defined($section) ) {
        $section = $page;
        $page    = undef;
    }
    else {
        $page    = undef unless $page;
        $section = undef unless $section;
    }
    return ( $page, $section );
}

sub get_elements_from_text {
    my ( $parser, $text, $line_num ) = @_;
    my @childs = ();

    #process paragrapth
    if ( my $root = $parser->parse_text( $text, $line_num ) ) {
        foreach my $node ( $root->children ) {
            unless ( ref($node) ) {
                push @childs, $parser->mk_characters($node);
            }
            else {
                my $elem = $parser->mk_element('code');
                my $attr = $elem->attrs_by_name;

                #handle L
                if ( $node->cmd_name() eq 'L' ) {
                    my $lattr = $parser->parse_link( $node->raw_text );
                    %{$attr} = %{$lattr};
                }
                $attr->{name} = $node->cmd_name();

                #set content
                $elem->{TITLE}    = $node->raw_text;
                $elem->{LINE_NUM} = $line_num;
                $elem->add_content( $parser->mk_cdata( $node->raw_text ) );
                push @childs, $elem;
            }
        }
    }
    return @childs;
}

sub stack {
    my $self = shift;
    $self->{STACK} = [] unless exists $self->{STACK};
    return $self->{STACK};
}

sub push_elem {
    my ( $self, $elem ) = @_;
    push @{ $self->stack }, $elem;
}

sub pop_elem {
    my ( $self, $elem ) = @_;
    pop @{ $self->stack };
}

sub _current {
    my $self = shift;
    return $self->stack->[-1];
}

#process levels
# $self->_process( <element> )
sub _process {
    my $self = shift;
    my ($elem) = @_;
#    warn "process ". $elem . "at " . $elem->local_name;
#    warn (ref($elem) eq 'HASH') ?  $elem->{type} : $elem->local_name;

    #get element for current level
    if ( my $current = $self->_current ) {
        $current->add_content($elem);
    }
    else {
        $self->start_element($elem);
        $self->end_element($elem);

    }

}

sub _start_elem {
    my ( $self, $elem ) = @_;
    $self->push_elem($elem);
}

sub _stop_elem {
    my ( $self, $elem ) = @_;
    if ( my $current = $self->pop_elem ) {
        if ($elem) {

            #check stack
            my $open_name  = $current->local_name;
            my $close_name = $elem->local_name;
            die
"Stack error: got unexpected: $elem->{COMMAND} at line: $elem->{LINE_NUM}"
              . ". In stack: $current->{COMMAND} ( from line: $current->{LINE_NUM} )"
              unless $open_name eq $close_name;
        }

        #special handle format tag
        if ( $current->local_name eq 'begin' ) {
            if ( my $cdata = delete $current->{TEXT_BLOCK} ) {
                $current->add_content( $self->mk_cdata($cdata) );
            }
        }
        $self->_process($current);
    }

}

sub command {
    my ( $parser, $command, $paragraph, $line_num ) = @_;
    my $self = $parser;
    $paragraph =~ s/\s+$//ms if defined $paragraph and $command ne 'for';
    my $elem = $parser->mk_element($command);

    #save para at element
    $elem->{TITLE}    = $paragraph;
    $elem->{LINE_NUM} = $line_num;
    $elem->{COMMAND}  = $command;
    $elem->{ID}       = $self->_make_uniq_id($paragraph);

    #    =begin html string
    #    adasdasd
    #    =end
    #convert to
    #    <format name="html" params=>"string">
    #        adasdasd
    #    </format>

    #special handle format tags begin and for
    # =begin   =for
    # =end
    if ( $command =~ /^begin|for$/ ) {
        $elem = $parser->mk_element('begin');
        my $attr = $elem->attrs_by_name;
        my ( $format_name, $format_params ) =
          $paragraph =~ m/\s*(\w+)(?:\s+?(.*))?$/gis;
        $attr->{name} = $format_name;
        if ( $command eq 'for' ) {
            my $content = $format_params;
            $format_params = '';
            $elem->add_content( $parser->mk_cdata($content) );
        }
        $attr->{params} = $format_params;
        $self->_start_elem($elem);
        if ( $command eq 'for' ) {
            $self->command( 'end', $format_name, $line_num );
        }
    }
    elsif ( $command eq 'end' ) {
        $elem->local_name('begin');
        $self->_stop_elem($elem);

    }
    elsif ( $command =~ /head(\d)/ ) {

        #if
        my $to_level = $1;

        #get current level from head from stack
        my $current_head =
          $self->_current ? $self->_current->local_name : "NONE";
        my ($current_level) = $current_head =~ /head(\d+)/;
        unless ( defined $current_level ) {

            #diag 'no current level for' . $current_head;
            die "check syntax before line $line_num for command: $current_head"
              unless $current_head =~ /^NONE|pod$/;
            $current_level = 0;
        }

        my $title = $parser->mk_element('title');
        $title->add_content(
            $parser->get_elements_from_text( $paragraph, $line_num ) );
        $elem->add_content($title);
        if ( $current_level < $to_level ) {

            #up level
            #=head1
            #=head2
            #set current stack
            die
"found step more then 1 level near =head$to_level at line: $line_num "
              if $to_level - $current_level > 1;
            $self->_start_elem($elem);
        }
        elsif ( $current_level == $to_level ) {
            $self->_stop_elem( $self->_current );

            #set current head at stack
            $self->_start_elem($elem);
        }
        else {

            # $current_level > $to_level
            #=head2
            #=head3
            #=head1
            #flush levels
            for ( 0 .. $current_level - $to_level ) {
                $self->_stop_elem( $self->_current );
            }
            $self->_start_elem($elem);
        }
    }
    elsif ( $command eq 'item' ) {
        my $cur_elem = $self->_current;
        unless ($cur_elem) {
            die "error near line: $line_num : =item not in over";
        }
        my $cur_name = $cur_elem->local_name;
        if ( $cur_name eq 'item' ) {
            $self->_stop_elem( $self->mk_element('item') );
        }
        elsif ( $cur_name ne 'over' ) {
            die "error near line: $line_num : =item not in over";
        }
        my $title = $parser->mk_element('title');
        $title->add_content(
            $parser->get_elements_from_text( $paragraph, $line_num ) );
        $elem->add_content($title);
        $self->_start_elem($elem);

    }
    elsif ( $command =~ /^over|pod$/ ) {
        $self->_start_elem($elem);
    }
    elsif ( $command eq 'back' ) {

        #close previus item
        my $current = $self->_current;
        if ( $current && $current->local_name eq 'item' ) {
            $self->_stop_elem($current);
        }
        $elem->local_name('over');
        $self->_stop_elem($elem);
    }
    elsif ( $command eq 'cut' ) {
        $elem->local_name('pod');

        #diag "Close!!";
        #die "aaa";
        $self->_stop_elem($elem);

    }
    else {
        die "Not handled tag $command : $paragraph";
    }
}

sub verbatim {
    my ( $parser, $paragraph, $line_num ) = @_;
    if ( $parser->_current && $parser->_current->local_name eq 'begin' ) {
        $parser->_current->{TEXT_BLOCK} .= $paragraph;
        return undef;
    }
    my $elem =
      $parser->mk_element('verbatim')
      ->add_content( $parser->mk_cdata($paragraph) );
    $parser->_process($elem);
}

sub textblock {
    my ( $parser, $paragraph, $line_num ) = @_;
    unless ( $parser->_current && $parser->_current->local_name eq 'begin' ) {
        $paragraph =~ s/\s+$//ms;
    }
    else {
        $parser->_current->{TEXT_BLOCK} .= $paragraph;
        return undef;
    }
    my $elem =
      $parser->mk_element('para')
      ->add_content( $parser->get_elements_from_text( $paragraph, $line_num ) );
    $parser->_process($elem)

}

=head2 _make_id($text[, $base_id])

Function will construct an element id string. Id string is composed of
C<< join (':', $base_id || $parser->{base_id} , $text) >>, where C<$text> in most cases
is the pod heading text.

The xml id string has strict format. Checkout L</"cleanup_id"> function for
specification.

=cut

sub _make_id {
    my $parser  = shift;
    my $text    = shift || '';
    my $base_id = shift || $parser->{base_id} || '';

    # trim text spaces
    $text    =~ s/^\s*//xms;
    $text    =~ s/\s*$//xms;
    $base_id =~ s/^\s*//xms;
    $base_id =~ s/\s*$//xms;

    return _cleanup_id( join( ':', $base_id, $text ) );
}

=head2 _make_uniq_id($text)

Calls C<< $parser->make_id($text) >> and checks if such id was already
generated. If so, generates new one by adding _i1 (or _i2, i3, ...) to the id
string. Return value is new uniq id string.

=cut

sub _make_uniq_id {
    my $parser = shift;
    my $text   = shift;

    my $id_string = $parser->_make_id($text);

    # prevent duplicate ids
    my $ids_used = $parser->{'ids_used'} || {};



( run in 0.845 second using v1.01-cache-2.11-cpan-39bf76dae61 )