File Coverage

blib/lib/XML/XOXO/Parser.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             package XML::XOXO::Parser;
2 1     1   6 use strict;
  1         1  
  1         45  
3 1     1   5 use base qw( XML::Parser );
  1         2  
  1         2120  
4              
5             my %HANDLERS;
6              
7             BEGIN {
8             no strict 'refs';
9             map {
10             $HANDLERS{ 'start_' . $_ } = \&{ __PACKAGE__ . '::start_' . $_ };
11             $HANDLERS{ 'end_' . $_ } = \&{ __PACKAGE__ . '::end_' . $_ };
12             } qw( li a dl dt dd ol ul);
13             }
14              
15             #--- constructor
16              
17             sub new {
18             my ( $class, %a ) = @_;
19             $a{NoExpand} = 1;
20             $a{ParamEnt} = 0;
21             delete $a{strict}; # precautionary while not operational.
22             my $self = $class->SUPER::new(%a);
23             bless( $self, $class );
24             no strict 'refs';
25             map { $self->setHandlers( $_, \&{$_} ) } qw( Init Start Char End Final );
26             $self;
27             }
28              
29             #--- XML::Parser handlers
30              
31             sub Init {
32             my $xp = shift;
33             $xp->{xostack} = [];
34             $xp->{textstack} = [];
35             $xp->{'return'} = [];
36             }
37              
38             sub Start {
39             $HANDLERS{ 'start_' . $_[1] }->(@_)
40             if $HANDLERS{ 'start_' . $_[1] };
41             }
42              
43             sub Char {
44             $_[0]->{textstack}->[-1] .= $_[1]
45             if defined( $_[0]->{textstack}->[-1] );
46             }
47              
48             sub End {
49             $HANDLERS{ 'end_' . $_[1] }->(@_)
50             if $HANDLERS{ 'end_' . $_[1] };
51             }
52              
53             sub Final {
54             delete $_[0]->{textstack};
55             delete $_[0]->{xostack};
56             $_[0]->{'return'};
57             }
58              
59             #--- tag handlers
60              
61             {
62             my $start_list = sub {
63             my ( $xp, $tag, $a ) = @_;
64              
65             # strict not working, but harmless left in.
66             unless ( $xp->{strict} && ( !$a->{class} || $a->{class} ne 'xoxo' ) ) {
67             my $node = XML::XOXO::Node->new;
68             $node->name($tag);
69             my $parent = $xp->{xostack}->[-1];
70             unless ($parent) {
71             push( @{ $xp->{'return'} }, $node );
72             } else {
73             $node->parent($parent);
74             $parent->contents( [] ) unless $parent->contents;
75             push( @{ $parent->contents }, $node );
76             }
77             push( @{ $xp->{xostack} }, $node );
78             } else {
79              
80             # $xp->skip_until($xp->element_index); # why doesn't this work?
81             }
82             };
83             *start_ol = $start_list;
84             *start_ul = $start_list;
85             }
86              
87             sub end_ol { pop( @{ $_[0]->{xostack} } ) }
88             sub end_ul { pop( @{ $_[0]->{xostack} } ) }
89              
90             sub start_li {
91             my ( $xp, $tag ) = @_;
92             my $node = XML::XOXO::Node->new;
93             $node->name($tag);
94             my $parent = $xp->{xostack}->[-1];
95             $node->parent($parent);
96             $parent->contents( [] ) unless $parent->contents;
97             push( @{ $xp->{xostack} }, $node );
98             push( @{ $node->parent->contents }, $node );
99             push( @{ $xp->{textstack} }, '' );
100             }
101              
102             sub end_li {
103             my ( $xp, $tag ) = @_;
104             my $node = pop( @{ $xp->{xostack} } );
105             my $val = strip_ws( pop( @{ $xp->{textstack} } ) );
106             $node->attributes->{text} = $val if length($val);
107             }
108              
109             sub start_a {
110             my ( $xp, $tag, %a ) = @_;
111             map { $a{$_} = lc( $a{$_} ) if $a{$_} } qw( rel type );
112             if ( $a{href} ) {
113             $a{url} = $a{href};
114             delete $a{href};
115             }
116             my $node = $xp->{xostack}->[-1];
117             map { $node->attributes->{$_} = $a{$_} } keys %a;
118             push( @{ $xp->{textstack} }, '' );
119             }
120              
121             sub end_a {
122             my ( $xp, $tag ) = @_;
123             my $val = strip_ws( pop( @{ $xp->{textstack} } ) );
124             my $node = $xp->{xostack}->[-1];
125             if ($val) {
126             if ( defined $node->attributes->{title}
127             && $node->attributes->{title} eq $val ) {
128             $val = '';
129             } elsif ( defined $node->attributes->{url}
130             && $node->attributes->{url} eq $val ) {
131             $val = '';
132             } elsif ( length($val) ) { # correct handling with end_li???
133             $node->attributes->{text} = $val;
134             }
135             }
136             }
137              
138             sub start_dl { }
139             sub end_dl { }
140             sub start_dt { push( @{ $_[0]->{textstack} }, '' ) }
141             sub end_dt { }
142              
143             sub start_dd {
144             my ( $xp, $tag ) = @_;
145             push( @{ $xp->{textstack} }, '' );
146              
147             # hack to capture multi-valued properties
148             my $dummy = XML::XOXO::Node->new;
149             $dummy->name('DUMMY');
150             push( @{ $xp->{xostack} }, $dummy );
151             }
152              
153             sub end_dd {
154             my $xp = shift;
155             my $val = strip_ws( pop( @{ $xp->{textstack} } ) );
156             my $key = pop( @{ $xp->{textstack} } );
157              
158             # undo hack.
159             my $dummy = pop( @{ $xp->{xostack} } );
160             my $node = $xp->{xostack}->[-1];
161             if ( defined( $dummy->contents->[0] ) ) {
162             $val = $dummy->contents->[0];
163             $val->parent($node);
164             }
165              
166             # end undo.
167             return unless length($val);
168             $key = strip_ws($key);
169             $node->attributes->{$key} = $val;
170             }
171              
172             #--- utility
173              
174             sub strip_ws {
175             $_[0] =~ s/[\n\t\r]//gs;
176             $_[0] =~ s/^\s+//;
177             $_[0] =~ s/\s+$//;
178             $_[0];
179             }
180              
181             1;
182              
183             __END__