File Coverage

blib/lib/XML/Element.pm
Criterion Covered Total %
statement 18 87 20.6
branch 0 44 0.0
condition 0 28 0.0
subroutine 6 12 50.0
pod 4 4 100.0
total 28 175 16.0


line stmt bran cond sub pod time code
1             require 5;
2              
3             package XML::Element;
4 2     2   13 use warnings;
  2         4  
  2         61  
5 2     2   11 use strict;
  2         4  
  2         62  
6 2     2   1913 use HTML::Tagset ();
  2         3449  
  2         79  
7 2     2   3445 use HTML::Element 4.1 ();
  2         65540  
  2         67  
8 2     2   30 use Carp;
  2         4  
  2         187  
9              
10 2     2   11 use vars qw(@ISA $VERSION);
  2         4  
  2         2578  
11             $VERSION = '5.4';
12             @ISA = ('HTML::Element');
13              
14             # Init:
15             my %emptyElement = ();
16             foreach my $e (%HTML::Tagset::emptyElement) {
17             $emptyElement{$e} = 1
18             if substr( $e, 0, 1 ) eq '~' and $HTML::Tagset::emptyElement{$e};
19             }
20              
21             my $in_cdata = 0;
22             my $nillio = [];
23              
24             #--------------------------------------------------------------------------
25             #Some basic overrides:
26              
27 0     0     sub _empty_element_map { \%emptyElement }
28              
29             *_fold_case = \&HTML::Element::_fold_case_NOT;
30             *starttag = \&starttag_XML;
31             *endtag = \&endtag_XML;
32             *encoded_content = \$HTML::Element::encoded_content;
33             *_xml_escape = \&HTML::Element::_xml_escape;
34              
35             # TODO: override id with something that looks for xml:id too/instead?
36              
37             #--------------------------------------------------------------------------
38              
39             #TODO: test and document this:
40             # with no tagname set, assumes ALL all-whitespace nodes are ignorable!
41              
42             sub delete_ignorable_whitespace {
43 0     0 1   my $under_hash = $_[1];
44 0           my (@to_do) = ( $_[0] );
45              
46 0 0 0       if ( $under_hash and ref($under_hash) eq 'ARRAY' ) {
47 0           $under_hash = { map { ; $_ => 1 } @$under_hash };
  0            
48             }
49              
50 0           my $all = !$under_hash;
51 0           my ( $i, $this, $children );
52 0           while (@to_do) {
53 0           $this = shift @to_do;
54 0   0       $children = $this->content || next;
55 0 0 0       if ( ( $all or $under_hash->{ $this->tag } )
      0        
56             and @$children )
57             {
58 0           for ( $i = $#$children; $i >= 0; --$i ) {
59              
60             # work backwards thru the list
61 0 0         next if ref $children->[$i];
62 0 0         if ( $children->[$i] =~ m<^\s*$>s ) { # all WS
63 0           splice @$children, $i, 1; # delete it.
64             }
65             }
66             }
67 0           unshift @to_do, grep ref($_), @$children; # recurse
68             }
69              
70 0           return;
71             }
72              
73             ## copied from HTML::Element to support CDATDA
74             sub starttag_XML {
75 0     0 1   my ($self) = @_;
76              
77             # and a third parameter to signal emptiness?
78              
79 0           my $name = $self->{'_tag'};
80              
81 0 0         return $self->{'text'} if $name eq '~literal';
82 0 0         return '{'text'} . '>' if $name eq '~declaration';
83 0 0         return "{'text'} . "?>" if $name eq '~pi';
84              
85 0 0         if ( $name eq '~comment' ) {
86 0 0 0       if ( ref( $self->{'text'} || '' ) eq 'ARRAY' ) {
87              
88             # Does this ever get used? And is this right?
89 0           $name = join( ' ', @{ $self->{'text'} } );
  0            
90             }
91             else {
92 0           $name = $self->{'text'};
93             }
94 0           $name =~ s/--/--/g; # can't have double --'s in XML comments
95 0           return "";
96             }
97              
98 0 0         if ( $name eq '~cdata' ) {
99 0           $in_cdata = 1;
100 0           return "
101             }
102              
103 0           my $tag = "<$name";
104 0           my $val;
105 0           for ( sort keys %$self ) { # predictable ordering
106 0 0 0       next if !length $_ or m/^_/s or $_ eq '/';
      0        
107              
108             # Hm -- what to do if val is undef?
109             # I suppose that shouldn't ever happen.
110 0 0         next if !defined( $val = $self->{$_} ); # or ref $val;
111 0           _xml_escape($val);
112 0           $tag .= qq{ $_="$val"};
113             }
114 0 0         @_ == 3 ? "$tag />" : "$tag>";
115             }
116              
117             ## copied from HTML::Element to support CDATDA
118             sub endtag_XML {
119 0     0 1   my ($self) = @_;
120              
121             # and a third parameter to signal emptiness?
122              
123 0           my $name = $self->{'_tag'};
124 0 0         if ( $name eq '~cdata' ) {
125 0           $in_cdata = 0;
126 0           return "]]>";
127             }
128              
129 0           "{'_tag'}>";
130             }
131              
132             ## copied from HTML::Element to support CDATDA
133             sub as_XML {
134              
135 0     0 1   my ($self) = @_;
136              
137             #my $indent_on = defined($indent) && length($indent);
138 0           my @xml = ();
139 0           my $empty_element_map = $self->_empty_element_map;
140              
141 0           my ( $tag, $node, $start ); # per-iteration scratch
142             $self->traverse(
143             sub {
144 0     0     ( $node, $start ) = @_;
145 0 0         if ( ref $node ) { # it's an element
146 0           $tag = $node->{'_tag'};
147 0 0         if ($start) { # on the way in
148              
149 0           foreach my $attr ( $node->all_attr_names() ) {
150 0 0 0       croak("$tag has an invalid attribute name '$attr'")
151             unless ( $attr eq '/'
152             || $self->_valid_name($attr) );
153             }
154              
155 0 0 0       if ( $empty_element_map->{$tag}
  0 0          
156             and !@{ $node->{'_content'} || $nillio } )
157             {
158 0           push( @xml, $node->starttag_XML( undef, 1 ) );
159             }
160             else {
161 0           push( @xml, $node->starttag_XML(undef) );
162             }
163             }
164             else { # on the way out
165 0 0 0       unless ( $empty_element_map->{$tag}
  0 0          
166             and !@{ $node->{'_content'} || $nillio } )
167             {
168 0           push( @xml, $node->endtag_XML() );
169             } # otherwise it will have been an <... /> tag.
170             }
171             }
172             else { # it's just text
173 0 0         _xml_escape($node) unless ($in_cdata);
174 0           push( @xml, $node );
175             }
176 0           1; # keep traversing
177             }
178 0           );
179              
180 0           join( '', @xml, "\n" );
181             }
182              
183             #--------------------------------------------------------------------------
184              
185             1;
186              
187             __END__