File Coverage

blib/lib/HTML/TreeBuilder/LibXML.pm
Criterion Covered Total %
statement 96 97 98.9
branch 23 26 88.4
condition 9 15 60.0
subroutine 25 26 96.1
pod 0 13 0.0
total 153 177 86.4


line stmt bran cond sub pod time code
1             package HTML::TreeBuilder::LibXML;
2 23     23   2833441 use strict;
  23         46  
  23         910  
3 23     23   143 use warnings;
  23         47  
  23         1834  
4             our $VERSION = '0.28';
5 23     23   149 use Carp ();
  23         48  
  23         538  
6 23     23   169 use base 'HTML::TreeBuilder::LibXML::Node';
  23         129  
  23         12816  
7 23     23   16294 use XML::LibXML;
  23         1575353  
  23         193  
8 23     23   4815 use 5.008001;
  23         89  
9              
10             sub new {
11 44     44 0 1302940 my $class = shift;
12 44         190 bless {}, $class;
13             }
14              
15             sub new_from_content {
16 29     29 0 2933472 my $class = shift;
17 29         135 my $self = $class->new;
18 29         140 for my $content (@_) {
19 31         127 $self->parse($content);
20             }
21 29         139 $self->eof;
22              
23 29         488 return $self;
24             }
25              
26             sub new_from_file {
27 1     1 0 1217 my $class = shift;
28 1         4 my $self = $class->new;
29 1         4 $self->parse_file(@_);
30 1         33 return $self;
31             }
32              
33             my $PARSER;
34             sub _parser {
35 43 100   43   127 unless ($PARSER) {
36 19         201 $PARSER = XML::LibXML->new();
37 19         539 $PARSER->recover(1);
38 19         482 $PARSER->recover_silently(1);
39 19         635 $PARSER->keep_blanks(1);
40 19         400 $PARSER->expand_entities(1);
41 19         341 $PARSER->no_network(1);
42             }
43 43         401 $PARSER;
44             }
45              
46             sub parse {
47 45     45 0 228 my ($self, $html) = @_;
48 45         378 $self->{_content} .= $html;
49             }
50              
51             sub parse_content {
52 1     1 0 2 my $self = shift;
53 1         6 $self->parse($_[0]);
54 1         12 $self->eof;
55             }
56              
57             sub parse_file {
58 1     1 0 3 my $self = shift;
59 1 50       76 open (my $fh, '<', $_[0]) or die "Can't open $_[0]: $!\n";
60 1         3 my $content = do { local $/; <$fh> };
  1         6  
  1         69  
61 1         6 $self->parse_content($content);
62             }
63              
64             sub eof {
65 43     43 0 137 my ($self, ) = @_;
66 43 100 66     323 $self->{_content} = ' ' if defined $self->{_content} && $self->{_content} eq ''; # HACK
67 43 100       308 $self->{_implicit_html} = 1 unless $self->{_content} =~ / was inserted
68 43 100       247 $self->{_implicit_doctype} = 1 unless $self->{_content} =~ /
69 43         164 my $doc = $self->_parser->parse_html_string($self->{_content});
70 43         16634 $self->{node} = $self->_documentElement($doc);
71             }
72              
73             sub _documentElement {
74 43     43   123 my($self, $doc) = @_;
75 43   66     1678 return $doc->documentElement || do {
76             my $elem = $doc->createElement("html");
77             $elem->appendChild($doc->createElement("body"));
78             $elem;
79             };
80             }
81              
82             sub elementify {
83 1     1 0 438 bless shift, 'HTML::TreeBuilder::LibXML::Node';
84             }
85              
86             sub guts {
87 27     27 0 139 my ($self, $destructive) = @_;
88              
89 28         1328 my @out = $self->{_implicit_html} ? map { $_->nonBlankChildNodes } $self->{node}->findnodes('/html/head | /html/body')
90 27 100       209 : $self->{node};
91              
92 27 100 66     424 if ($destructive && @out > 0) {
93 8         219 my $doc = XML::LibXML->createDocument;
94 8 50 33     175 if (!$self->{_implicit_doctype} && (my $dtd = $out[0]->ownerDocument->internalSubset)) {
95 0         0 $doc->createInternalSubset( $dtd->getName, $dtd->publicId, $dtd->systemId );
96             }
97 8         44 $doc->setDocumentElement($out[0]); # 1st child
98 8         186 $out[0]->addSibling($_) foreach @out[1..$#out];
99             }
100            
101 27 100       471 return map { HTML::TreeBuilder::LibXML::Node->new($_) } @out if wantarray; # one simple normal case.
  30         122  
102 8 50       32 return unless @out;
103            
104 8         36 my $doc = XML::LibXML->createDocument;
105 8 100 66     222 if (!$self->{_implicit_doctype} && (my $dtd = $out[0]->ownerDocument->internalSubset)) {
106 1         53 $doc->createInternalSubset( $dtd->getName, $dtd->publicId, $dtd->systemId );
107             }
108              
109 8 100       39 if (@out == 1) {
110 5         49 $doc->adoptNode($out[0]);
111 5         13 $doc->setDocumentElement($out[0]);
112 5         184 return HTML::TreeBuilder::LibXML::Node->new($out[0]);
113             }
114            
115 3         25 my $div = $doc->createElement('div'); # TODO put the _implicit flag somewhere, to be compatible with HTML::TreeBuilders
116 3         15 $doc->setDocumentElement($div);
117 3         91 $div->appendChild($_) for @out;
118            
119 3         74 return HTML::TreeBuilder::LibXML::Node->new($div);
120             }
121              
122             sub disembowel {
123 7     7 0 65 my ($self) = @_;
124 7         26 $self->guts(1);
125             }
126              
127             sub replace_original {
128 2     2 0 396764 require HTML::TreeBuilder::XPath;
129              
130 2         34 my $orig = HTML::TreeBuilder::XPath->can('new');
131              
132 23     23   217 no warnings 'redefine';
  23         55  
  23         6495  
133             *HTML::TreeBuilder::XPath::new = sub {
134 2     2   23 HTML::TreeBuilder::LibXML->new();
135 2         12 };
136              
137 2 100       10 if (defined wantarray) {
138             return HTML::TreeBuilder::LibXML::Destructor->new(
139 1     1   9 sub { *HTML::TreeBuilder::XPath::new = $orig } );
  1         7  
140             }
141 1         10 return;
142             }
143              
144             # The HTML::TreeBuilder has this method and it is needed to us for web-scraper module
145       1 0   sub store_comments { }
146       0 0   sub ignore_unknown { }
147              
148             package # hide from cpan
149             HTML::TreeBuilder::LibXML::Destructor;
150              
151             sub new {
152 1     1   15 my ( $class, $callback ) = @_;
153 1         5 bless { cb => $callback }, $class;
154             }
155              
156             sub DESTROY {
157 1     1   313 my $self = shift;
158 1         5 $self->{cb}->();
159             }
160              
161             1;
162             __END__