File Coverage

blib/lib/XML/All.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package XML::All;
2              
3 1     1   22 use 5.006001;
  1         4  
  1         39  
4 1     1   5 use strict;
  1         2  
  1         28  
5 1     1   6 use warnings;
  1         2  
  1         37  
6 1     1   552 use XML::Twig ();
  0            
  0            
7             use Tie::Simple ();
8             use Exporter::Lite ();
9             use Class::InsideOut ();
10              
11             our @EXPORT = qw( xml );
12             our $VERSION = '0.02';
13              
14             Class::InsideOut::private(twig => my %twig);
15             Class::InsideOut::private(sv => my %sv);
16             Class::InsideOut::private(av => my %av);
17             Class::InsideOut::private(cv => my %cv);
18              
19             use XML::Literal sub {
20             my $obj = Class::InsideOut::register( bless \(my $s), __PACKAGE__ );
21             my $xml = XML::Twig->new;
22             $xml->parse($_[0]);
23             $twig{ Class::InsideOut::id($obj) } = $xml->root;
24             bless($obj);
25             };
26              
27             my $id = \&Class::InsideOut::id;
28              
29             my $xmlify = sub {
30             my $xml = XML::Twig->new;
31             $xml->parse($_[0]);
32             return $xml->root;
33             };
34              
35             my $wrap = sub {
36             my $res = Class::InsideOut::register( bless \(my $s), __PACKAGE__ );
37             $twig{ $id->($res) } = $_[0];
38             bless($res);
39             };
40              
41             my $unwrap = sub {
42             $twig{$id->($_[0]) || do {
43             my $elt = XML::Twig::Elt->new(XML::Twig::PCDATA);
44             $elt->_set_pcdata($_[0]);
45             return $elt;
46             }};
47             };
48              
49             sub xml {
50             if (ref($_[0]) and ref($_[0])->isa(__PACKAGE__)) {
51             my $obj = $twig{$id->(shift(@_))};
52             my @children = map { $wrap->($_) } $obj->children(join('', 'xml', map { "[$_]" } @_ ));
53             wantarray ? @children : $children[0];
54             }
55             elsif ($_[0] =~ /^\s*
56             my $xml = XML::Twig->new;
57             $xml->parse($_[0]);
58             return $wrap->($xml);
59             }
60             else {
61             my $xml = XML::Twig->new;
62             $xml->parsefile($_[0]);
63             return $wrap->($xml);
64             }
65             }
66              
67             sub AUTOLOAD :lvalue {
68             my $meth = our $AUTOLOAD;
69             $meth =~ s/.*:://;
70              
71             return if $meth eq 'DEMOLISH' or $meth eq 'DESTROY';
72              
73             my $xml = shift;
74             my $obj = $twig{$id->($xml)};
75             my @children = $obj->children(join('', $meth, map { "[$_]" } @_ ));
76             if (wantarray and @children != 1) {
77             @children = map { $wrap->($_) } @children;
78             return @children;
79             }
80             else {
81             $children[0] or return undef;
82             my $res = Class::InsideOut::register( bless \(my $s), __PACKAGE__ );
83             $twig{ $id->($res) } = $children[0];
84             bless($res);
85             return $res;
86             }
87             }
88              
89             my $op;
90             BEGIN { $op = sub {
91             my ($sym, $call) = @_;
92             return (
93             $sym => sub {
94             my $obj = $twig{$id->($_[0])} || $xmlify->($_[0]);
95             my $tgt = $twig{$id->($_[1])} || $xmlify->($_[1]);
96             ($obj, $tgt) = ($tgt, $obj) if $_[2];
97             my $cpy = $obj->copy;
98             $call->($cpy, $tgt);
99             $wrap->($cpy);
100             },
101             "$sym=" => sub {
102             my $obj = $twig{$id->($_[0])} || $xmlify->($_[0]);
103             my $tgt = $twig{$id->($_[1])} || $xmlify->($_[1]);
104              
105             ($obj, $tgt) = ($tgt, $obj) if $_[2];
106             $call->($obj, $tgt);
107             $wrap->($obj);
108             },
109             );
110             } };
111              
112             use overload (
113             '${}' => sub {
114             $sv{$id->($_[0])} ||= do {
115             my $obj = $twig{$id->($_[0])};
116             tie my $res, 'Tie::Simple', undef,
117             FETCH => sub { $obj->tag },
118             STORE => sub {
119             no warnings 'uninitialized';
120             length($_[1]) ? $obj->set_tag($_[1]) : $obj->erase
121             };
122             \$res;
123             };
124             },
125             '@{}' => sub {
126             $av{$id->($_[0])} ||= do {
127             my $obj = $twig{$id->($_[0])};
128             tie my @res, 'Tie::Simple', undef,
129             FETCH => sub {
130             $wrap->($obj->child($_[1]))
131             },
132             STORE => sub {
133             my $tgt = $unwrap->($_[2]);
134             if (my $src = $obj->child($_[1])) {
135             $src->replace_with($tgt);
136             }
137             elsif ($_[1] == $obj->children_count) {
138             $tgt->paste_last_child($obj);
139             }
140             else {
141             die "Index out of bounds";
142             }
143             },
144             FETCHSIZE => sub { $obj->children_count },
145             DELETE => sub { $obj->child($_[1])->delete },
146             CLEAR => sub { $obj->cut_children },
147             PUSH => sub {
148             shift;
149             foreach my $xml (@_) {
150             my $tgt = $twig{$id->($xml)};
151             $tgt->paste_last_child($obj);
152             }
153             },
154             POP => sub {
155             my $tgt = $obj->last_child or return undef;
156             $tgt->cut;
157             $wrap->($tgt);
158             },
159             SHIFT => sub {
160             my $tgt = $obj->first_child or return undef;
161             $tgt->cut;
162             $wrap->($tgt);
163             },
164             UNSHIFT => sub {
165             shift;
166             foreach my $xml (reverse @_) {
167             my $tgt = $twig{$id->($xml)};
168             $tgt->paste_first_child($obj);
169             }
170             };
171             \@res;
172             };
173             },
174             '%{}' => sub {
175             my $obj = $twig{$id->($_[0])};
176             $obj->atts;
177             },
178             '&{}' => sub {
179             my $obj_id = $id->($_[0]);
180             $cv{$obj_id} ||= do {
181             my $obj = $twig{$obj_id};
182             sub {
183             if (@_) {
184             my $xml = XML::Twig->new;
185             my %args = @_;
186             foreach my $key (keys %args) {
187             my $code = $args{$key};
188             $args{$key} = sub {
189             my $res = Class::InsideOut::register( bless \(my $s), __PACKAGE__ );
190             $twig{ $id->($res) } = $_[1];
191             local $_ = bless($res);
192             $code->();
193             }
194             }
195             $xml->setTwigHandlers( \%args );
196             $xml->parse($obj->sprint);
197             $twig{ $obj_id } = $xml->root;
198             }
199             else {
200             join '', map { $_->is_text ? $_->text : () } $obj->children
201             }
202             };
203             };
204             },
205             '0+' => sub {
206             my $obj = $twig{$id->($_[0])};
207             $obj->text;
208             },
209             '""' => sub {
210             my $obj = $twig{$id->($_[0])};
211             $obj->sprint;
212             },
213             'bool' => sub {
214             my $obj = $twig{$id->($_[0])};
215             $obj->is_text ? $obj->text : 1;
216             },
217             $op->('+' => sub { $_[1]->paste_last_child($_[0])}),
218             $op->('-' => sub { $_[0]->cut_children($_[1]->tag)}),
219             ), fallback => 1;
220              
221             1;
222              
223             __END__