File Coverage

blib/lib/XML/RSS/Parser.pm
Criterion Covered Total %
statement 53 56 94.6
branch 3 4 75.0
condition 4 5 80.0
subroutine 12 15 80.0
pod 9 10 90.0
total 81 90 90.0


line stmt bran cond sub pod time code
1             package XML::RSS::Parser;
2 1     1   925 use strict;
  1         3  
  1         45  
3              
4 1     1   904 use XML::Elemental;
  1         8995  
  1         39  
5 1     1   21 use base qw( Class::ErrorHandler );
  1         3  
  1         1058  
6              
7 1     1   418 use vars qw( $VERSION );
  1         2  
  1         790  
8             $VERSION = 4.0;
9              
10             my %xpath_prefix = (
11             admin => "http://webns.net/mvcb/",
12             ag => "http://purl.org/rss/1.0/modules/aggregation/",
13             annotate => "http://purl.org/rss/1.0/modules/annotate/",
14             atom => "http://www.w3.org/2005/Atom",
15             audio => "http://media.tangent.org/rss/1.0/",
16             cc => "http://web.resource.org/cc/",
17             company => "http://purl.org/rss/1.0/modules/company",
18             content => "http://purl.org/rss/1.0/modules/content/",
19             cp => "http://my.theinfo.org/changed/1.0/rss/",
20             dc => "http://purl.org/dc/elements/1.1/",
21             dcterms => "http://purl.org/dc/terms/",
22             email => "http://purl.org/rss/1.0/modules/email/",
23             ev => "http://purl.org/rss/1.0/modules/event/",
24             feedburner => "http://rssnamespace.org/feedburner/ext/1.0",
25             foaf => "http://xmlns.com/foaf/0.1/",
26             image => "http://purl.org/rss/1.0/modules/image/",
27             itunes => "http://www.itunes.com/DTDs/Podcast-1.0.dtd",
28             l => "http://purl.org/rss/1.0/modules/link/",
29             openSearch => "http://a9.com/-/spec/opensearchrss/1.0/",
30             rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
31             rdfs => "http://www.w3.org/2000/01/rdf-schema#",
32             'ref' => "http://purl.org/rss/1.0/modules/reference/",
33             reqv => "http://purl.org/rss/1.0/modules/richequiv/",
34             rss091 => "http://purl.org/rss/1.0/modules/rss091#",
35             search => "http://purl.org/rss/1.0/modules/search/",
36             slash => "http://purl.org/rss/1.0/modules/slash/",
37             ss => "http://purl.org/rss/1.0/modules/servicestatus/",
38             str => "http://hacks.benhammersley.com/rss/streaming/",
39             'sub' => "http://purl.org/rss/1.0/modules/subscription/",
40             sy => "http://purl.org/rss/1.0/modules/syndication/",
41             tapi => "http://api.technorati.com/dtd/tapi-001.xml#",
42             taxo => "http://purl.org/rss/1.0/modules/taxonomy/",
43             thr => "http://purl.org/rss/1.0/modules/threading/",
44             trackback => "http://madskills.com/public/xml/rss/module/trackback/",
45             wiki => "http://purl.org/rss/1.0/modules/wiki/",
46             xhtml => "http://www.w3.org/1999/xhtml",
47             xml => "http://www.w3.org/XML/1998/namespace/",
48              
49             creativeCommons => "http://backend.userland.com/creativeCommonsRssModule"
50             );
51             my %xpath_ns = reverse %xpath_prefix;
52              
53             sub new {
54 2     2 1 530 my $class = shift;
55 2         7 my $self = bless {}, $class;
56 2         10 my $params = {
57             Document => 'XML::RSS::Parser::Feed',
58             Element => 'XML::RSS::Parser::Element',
59             Characters => 'XML::RSS::Parser::Characters'
60             };
61 2         13 $self->{__parser} = XML::Elemental->parser($params);
62 2         99382 $self;
63             }
64              
65             sub register_ns_prefix {
66 2     2 1 686 my ($this, $prefix, $ns) = @_;
67 2         7 $xpath_prefix{$prefix} = $ns;
68 2         6 $xpath_ns{$ns} = $prefix;
69             }
70              
71 0     0 1 0 sub parse { _parse('parse', @_); }
72 0     0 1 0 sub parse_file { _parse('parse_file', @_); }
73 1     1 1 8 sub parse_string { _parse('parse_string', @_); }
74 0     0 1 0 sub parse_uri { _parse('parse_uri', @_); }
75              
76             sub _parse {
77 1     1   2 my $meth = shift;
78 1         2 my $e = shift;
79 1         1 my $doc;
80 1         3 eval { $doc = $e->{__parser}->$meth(@_) };
  1         7  
81 1 50       311 return $e->error($@) if ($@);
82 1         5 $e->rss_normalize($doc);
83             }
84              
85             #--- utils
86              
87 24     24 1 64 sub prefix { $xpath_ns{$_[1]} }
88 2     2 1 13 sub namespace { $xpath_prefix{$_[1]} }
89              
90             sub ns_qualify {
91 1     1 1 265 my ($this, $name, $ns) = @_;
92 1   50     4 $ns ||= '';
93 1         4 "{$ns}$name";
94             }
95              
96             # Since different RSS formats have slightly different tag hierarchies
97             # we make some alternations after processing so bring them all into
98             # line.
99             sub rss_normalize {
100 1     1 0 2 my $self = shift;
101 1         2 my $doc = shift;
102 1         5 my $ns = $doc->find_rss_namespace;
103 1         4 my $channel_name = "{$ns}channel";
104 1         4 my $root = $doc->contents->[0];
105 1         8 my @new_contents;
106             my $channel;
107 1         2 foreach (@{$root->contents}) {
  1         4  
108 7 100 100     55 if ($_->can('name') && ($_->name eq $channel_name)) {
109 1         11 $_->parent($doc);
110 1         4 $channel = $_;
111 1         5 $doc->contents([$_]);
112             } else {
113 6         18 push(@new_contents, $_);
114             }
115             }
116 1         3 map { $_->parent($channel) } @new_contents;
  6         33  
117 1         4 $channel->contents([@{$channel->contents}, @new_contents]);
  1         2  
118 1         13 $root->parent(undef);
119 1         5 $root->contents(undef);
120 1         10 $doc;
121             }
122              
123             1;
124              
125             __END__