File Coverage

blib/lib/XML/MyXML/Object.pm
Criterion Covered Total %
statement 144 159 90.5
branch 65 80 81.2
condition 19 22 86.3
subroutine 17 18 94.4
pod 0 12 0.0
total 245 291 84.1


line stmt bran cond sub pod time code
1             package XML::MyXML::Object;
2              
3 2     2   16 use strict;
  2         4  
  2         59  
4 2     2   8 use warnings;
  2         4  
  2         45  
5              
6 2     2   557 use Encode;
  2         10464  
  2         203  
7 2     2   14 use Carp;
  2         5  
  2         186  
8 2     2   15 use Scalar::Util qw/ weaken /;
  2         4  
  2         4118  
9              
10             our $VERSION = "1.06";
11              
12             sub new {
13 0     0 0 0 my $class = shift;
14 0         0 my $xml = shift;
15              
16 0         0 my $obj = XML::MyXML::xml_to_object($xml);
17 0         0 bless $obj, $class;
18 0         0 return $obj;
19             }
20              
21             sub _parse_description {
22 40     40   67 my ($desc) = @_;
23              
24 40         196 my ($tag, $attrs_str) = $desc =~ /\A([^\[]*)(.*)\z/g;
25 40         94 my %attrs = $attrs_str =~ /\[([^\]=]+)(?:=(\"[^"]*\"|[^"\]]*))?\]/g;
26 40         96 foreach my $value (values %attrs) {
27 5         14 $value =~ s/\A\"//;
28 5         12 $value =~ s/\"\z//;
29             }
30              
31 40         109 return ($tag, \%attrs);
32             }
33              
34             sub cmp_element {
35 83     83 0 147 my ($self, $desc) = @_;
36              
37             my ($tag, $attrs) = ref $desc
38 83 100       200 ? @$desc{qw/ tag attrs /}
39             : _parse_description($desc);
40              
41 83 100 100     872 ! length $tag or $self->{element} =~ /(\A|\:)\Q$tag\E\z/ or return 0;
42 51         159 foreach my $attr (keys %$attrs) {
43 14         29 my $val = $self->attr($attr);
44 14 50       27 defined $val or return 0;
45 14 100 66     63 ! defined $attrs->{$attr} or $attrs->{$attr} eq $val or return 0;
46             }
47              
48 44         135 return 1;
49             }
50              
51             sub children {
52 33     33 0 50 my $self = shift;
53 33         48 my $tag = shift;
54              
55 33 50       68 $tag = '' if ! defined $tag;
56              
57 33         40 my @all_children = grep { defined $_->{element} } @{$self->{content}};
  138         269  
  33         63  
58 33 50       74 length $tag or return @all_children;
59              
60 33         64 ($tag, my $attrs) = _parse_description($tag);
61 33         85 my $desc = { tag => $tag, attrs => $attrs };
62              
63 33         81 my @results = grep $_->cmp_element($desc), @all_children;
64              
65 33         130 return @results;
66             }
67              
68             sub path {
69 27     27 0 7440 my $self = shift;
70 27         48 my $path = shift;
71              
72 27         38 my @path;
73 27         45 my $orig_path = $path;
74 27         69 my $start_root = $path =~ m!\A/!;
75 27 100       84 $path = "/" . $path unless $start_root;
76 27         73 while (length $path) {
77 37         199 my $success = $path =~ s!\A/((?:[^/\[]*)?(?:\[[^\]=]+(?:=(?:\"[^"]*\"|[^"\]]*))?\])*)!!;
78 37         93 my $seg = $1;
79 37 50       69 if ($success) {
80 37         107 push @path, $seg;
81             } else {
82 0         0 croak "Invalid XML path: $orig_path";
83             }
84             }
85              
86 27         52 my @result = ($self);
87 27 100       57 if ($start_root) {
88 7 100       16 $self->cmp_element(shift @path) or return;
89             }
90 26         112 for (my $i = 0; $i <= $#path; $i++) {
91 29         107 @result = map $_->children( $path[$i] ), @result;
92 29 50       142 @result or return;
93             }
94 26 100       139 return wantarray ? @result : $result[0];
95             }
96              
97             sub text {
98 33     33 0 57 my $self = shift;
99 33 100 100     156 my $flags = (@_ and ref $_[-1]) ? pop() : {};
100 33 100       61 my $set_value = @_ ? defined $_[0] ? shift() : '' : undef;
    100          
101              
102 33 100       57 if (! defined $set_value) {
103 30         41 my $value = '';
104 30 100       58 if ($self->{content}) {
105 15         22 foreach my $child (@{ $self->{content} }) {
  15         28  
106 18         49 $value .= $child->value($flags);
107             }
108             }
109 30 100       56 if ($self->{value}) {
110 12         21 my $temp_value = $self->{value};
111 12 100       27 if ($flags->{strip}) { $temp_value = XML::MyXML::_strip($temp_value); }
  2         6  
112 12         23 $value .= $temp_value;
113             }
114 30         100 return $value;
115             } else {
116 3 100       10 if (length $set_value) {
117 1         5 my $entry = { value => $set_value, parent => $self };
118 1         5 weaken( $entry->{parent} );
119 1         3 bless $entry, 'XML::MyXML::Object';
120 1         6 $self->{content} = [ $entry ];
121             } else {
122 2         7 $self->{content} = [];
123             }
124             }
125             }
126              
127             *value = \&text;
128              
129             sub inner_xml {
130 6     6 0 25 my $self = shift;
131 6 100 100     30 my $flags = (@_ and ref $_[-1]) ? pop() : {};
132 6 100       17 my $set_xml = @_ ? defined $_[0] ? shift() : '' : undef;
    100          
133              
134 6 100       14 if (! defined $set_xml) {
135 3         7 my $xml = $self->to_xml($flags);
136 3         18 $xml =~ s/\A\<.*?\>//s;
137 3         13 $xml =~ s/\<\/[^\>]*\>\z//s;
138 3         18 return $xml;
139             } else {
140 3         10 my $xml = "
$set_xml
";
141 3         9 my $obj = XML::MyXML::xml_to_object($xml, $flags);
142 3         16 $self->{content} = [];
143 3 100       6 foreach my $child (@{ $obj->{content} || [] }) {
  3         16  
144 4         7 $child->{parent} = $self;
145 4         11 weaken( $child->{parent} );
146 4         6 push @{ $self->{content} }, $child;
  4         14  
147             }
148             }
149             }
150              
151             sub attr {
152 30     30 0 77 my $self = shift;
153 30         41 my $attrname = shift;
154 30         54 my ($set_to, $must_set, $flags);
155 30 100       57 if (@_) {
156 2         4 my $next = shift;
157 2 50       7 if (! ref $next) {
158 2         3 $set_to = $next;
159 2         3 $must_set = 1;
160 2         4 $flags = shift;
161             } else {
162 0         0 $flags = $next;
163             }
164             }
165 30   50     119 $flags ||= {};
166              
167 30 50       61 if (defined $attrname) {
168 30 100       49 if ($must_set) {
169 2 100       7 if (defined ($set_to)) {
170 1         3 $self->{attrs}{$attrname} = $set_to;
171 1         3 return $set_to;
172             } else {
173 1         4 delete $self->{attrs}{$attrname};
174 1         3 return;
175             }
176             } else {
177 28         53 my $attrvalue = $self->{attrs}->{$attrname};
178 28         111 return $attrvalue;
179             }
180             } else {
181 0         0 return %{$self->{attrs}};
  0         0  
182             }
183             }
184              
185             sub tag {
186 5     5 0 15 my $self = shift;
187 5   100     20 my $flags = shift || {};
188              
189 5         8 my $tag = $self->{element};
190 5 50       13 if (defined $tag) {
191 5 100       24 $tag =~ s/\A.*\:// if $flags->{strip_ns};
192 5         51 return $tag;
193             } else {
194 0         0 return undef;
195             }
196             }
197              
198             sub parent {
199 2     2 0 5 my $self = shift;
200              
201 2         10 return $self->{parent};
202             }
203              
204             sub simplify {
205 14     14 0 25 my $self = shift;
206 14   100     37 my $flags = shift || {};
207              
208 14         41 my $simple = XML::MyXML::_objectarray_to_simple([$self], $flags);
209 14 100       43 if (! $flags->{internal}) {
210 9         28 return $simple;
211             } else {
212 5 50       14 if (ref $simple eq 'HASH') {
    0          
213 5         26 return (values %$simple)[0];
214             } elsif (ref $simple eq 'ARRAY') {
215 0         0 return $simple->[1];
216             }
217             }
218             }
219              
220             sub to_xml {
221 21     21 0 1141 my $self = shift;
222 21   100     61 my $flags = shift || {};
223              
224 21 50       49 my $decl = $flags->{complete} ? ''."\n" : '';
225 21         72 my $xml = XML::MyXML::_objectarray_to_xml([$self]);
226 21 100       63 if ($flags->{tidy}) { $xml = XML::MyXML::tidy_xml($xml, { %$flags, bytes => 0, complete => 0, save => undef }); }
  3         20  
227 21         43 $xml = $decl . $xml;
228 21 50       53 if (defined $flags->{save}) {
229 0 0       0 open my $fh, '>', $flags->{save} or croak "Error: Couldn't open file '$flags->{save}' for writing: $!";
230 0         0 binmode $fh, ':encoding(UTF-8)';
231 0         0 print $fh $xml;
232 0         0 close $fh;
233             }
234 21 100       48 $xml = encode_utf8($xml) if $flags->{bytes};
235 21         104 return $xml;
236             }
237              
238             sub to_tidy_xml {
239 1     1 0 542 my $self = shift;
240 1   50     5 my $flags = shift || {};
241              
242 1         6 return $self->to_xml({ %$flags, tidy => 1 });
243             }
244              
245             1;