File Coverage

blib/lib/XML/Atom/Base.pm
Criterion Covered Total %
statement 212 225 94.2
branch 59 76 77.6
condition 22 33 66.6
subroutine 40 41 97.5
pod 0 23 0.0
total 333 398 83.6


line stmt bran cond sub pod time code
1             # $Id$
2              
3             package XML::Atom::Base;
4 22     22   670 use strict;
  22         69  
  22         722  
5 22     22   114 use base qw( XML::Atom::ErrorHandler Class::Data::Inheritable );
  22         38  
  22         10226  
6              
7 22     22   14917 use Encode;
  22         213163  
  22         1887  
8 22     22   6210 use XML::Atom;
  22         69  
  22         1347  
9 22     22   10513 use XML::Atom::Util qw( set_ns first nodelist childlist create_element );
  22         73  
  22         35410  
10              
11             __PACKAGE__->mk_classdata('__attributes', []);
12              
13             sub new {
14 215     215 0 18851 my $class = shift;
15 215         465 my $obj = bless {}, $class;
16 215 50       758 $obj->init(@_) or return $class->error($obj->errstr);
17 215         629 $obj;
18             }
19              
20             sub init {
21 215     215 0 356 my $obj = shift;
22 215         459 my %param = @_;
23 215 100 100     844 if (!exists $param{Namespace} and my $ns = $obj->element_ns) {
24 3         27 $param{Namespace} = $ns;
25             }
26 215         840 $obj->set_ns(\%param);
27 215         342 my $elem;
28 215 100       967 unless ($elem = $param{Elem}) {
29 53         82 if (LIBXML) {
30 53         585 my $doc = XML::LibXML::Document->createDocument('1.0', 'UTF-8');
31 53         191 my $ns = $obj->ns;
32 53         106 my ($ns_uri, $ns_prefix);
33 53 100 66     212 if ( ref $ns and $ns->isa('XML::Atom::Namespace') ) {
34 2         6 $ns_uri = $ns->{uri};
35 2         5 $ns_prefix = $ns->{prefix};
36             } else {
37 51         100 $ns_uri = $ns;
38             }
39 53 100 66     214 if ( $ns_uri and $ns_prefix ) {
40 2         8 $elem = $doc->createElement($obj->element_name);
41 2         25 $elem->setNamespace( $ns_uri, $ns_prefix, 1 );
42             } else {
43 51         124 $elem = $doc->createElementNS($obj->ns, $obj->element_name);
44             }
45 53         298 $doc->setDocumentElement($elem);
46             } else {
47             $elem = XML::XPath::Node::Element->new($obj->element_name);
48             my $ns = XML::XPath::Node::Namespace->new('#default' => $obj->ns);
49             $elem->appendNamespace($ns);
50             }
51             }
52 215         1919 $obj->{elem} = $elem;
53 215         505 $obj;
54             }
55              
56       0 0   sub element_name { }
57       176 0   sub element_ns { }
58              
59 366     366 0 1436 sub ns { $_[0]->{ns} }
60 673     673 0 5018 sub elem { $_[0]->{elem} }
61              
62             sub version {
63 81     81 0 591 my $atom = shift;
64 81         179 XML::Atom::Util::ns_to_version($atom->ns);
65             }
66              
67             sub content_type {
68 2     2 0 25 my $atom = shift;
69 2 100       7 if ($atom->version >= 1.0) {
70 1         5 return "application/atom+xml";
71             } else {
72 1         22 return "application/x.atom+xml";
73             }
74             }
75              
76             sub get {
77 72     72 0 125 my $obj = shift;
78 72         151 my($ns, $name) = @_;
79 72         214 my @list = $obj->getlist($ns, $name);
80 72         1325 return $list[0];
81             }
82              
83             sub getlist {
84 73     73 0 110 my $obj = shift;
85 73         130 my($ns, $name) = @_;
86 73 100       196 my $ns_uri = ref($ns) eq 'XML::Atom::Namespace' ? $ns->{uri} : $ns;
87 73         168 my @node = childlist($obj->elem, $ns_uri, $name);
88             return map {
89 73         1019 my $val = LIBXML ? $_->textContent : $_->string_value;
  75         633  
90 75 50       240 if ($] >= 5.008) {
91 75         455 require Encode;
92 75 50       302 Encode::_utf8_off($val) unless $XML::Atom::ForceUnicode;
93             }
94 75         341 $val;
95             } @node;
96             }
97              
98             sub add {
99 1     1 0 666 my $obj = shift;
100 1         4 my($ns, $name, $val, $attr) = @_;
101 1         6 return $obj->set($ns, $name, $val, $attr, 1);
102             }
103              
104             sub set {
105 43     43 0 80 my $obj = shift;
106 43         102 my($ns, $name, $val, $attr, $add) = @_;
107 43 100       138 my $ns_uri = ref $ns eq 'XML::Atom::Namespace' ? $ns->{uri} : $ns;
108 43         109 my @elem = childlist($obj->elem, $ns_uri, $name);
109 43 100 100     697 if (!$add && @elem) {
110 7         33 $obj->elem->removeChild($_) for @elem;
111             }
112 43         227 my $elem = create_element($ns, $name);
113 43 100       205 if (UNIVERSAL::isa($val, 'XML::Atom::Base')) {
114 13         99 if (LIBXML) {
115 13         38 for my $child ($val->elem->childNodes) {
116 15         419 $elem->appendChild($child->cloneNode(1));
117             }
118 13         223 for my $attr ($val->elem->attributes) {
119 22 100       375 next unless ref($attr) eq 'XML::LibXML::Attr';
120 9         116 $elem->setAttribute($attr->getName, $attr->getValue);
121             }
122             } else {
123             for my $child ($val->elem->getChildNodes) {
124             $elem->appendChild($child);
125             }
126             for my $attr ($val->elem->getAttributes) {
127             $elem->appendAttribute($attr);
128             }
129             }
130             } elsif (DATETIME && UNIVERSAL::isa($val, "DateTime")) {
131             return $obj->set($ns, $name, DateTime::Format::Atom->format_datetime($val), $attr, $add);
132             } else {
133 30         48 if (LIBXML) {
134 30         368 $elem->appendChild(XML::LibXML::Text->new($val));
135             } else {
136             $elem->appendChild(XML::XPath::Node::Text->new($val));
137             }
138             }
139 43         272 $obj->elem->appendChild($elem);
140 43 50       150 if ($attr) {
141 0         0 while (my($k, $v) = each %$attr) {
142 0         0 $elem->setAttribute($k, $v);
143             }
144             }
145 43         875 return $val;
146             }
147              
148             sub get_attr {
149 125     125 0 174 my $obj = shift;
150 125         221 my($attr) = @_;
151 125         265 my $val = $obj->elem->getAttribute($attr);
152 125 50       1568 if ($] >= 5.008) {
153 125         615 require Encode;
154 125 50       464 Encode::_utf8_off($val) unless $XML::Atom::ForceUnicode;
155             }
156 125         652 $val;
157             }
158              
159             sub set_attr {
160 47     47 0 82 my $obj = shift;
161 47 100       122 if (@_ == 2) {
    50          
162 45         105 my($attr, $val) = @_;
163 45         103 $obj->elem->setAttribute($attr => $val);
164             } elsif (@_ == 3) {
165 2         21 my($ns, $attr, $val) = @_;
166 2         14 my $attribute = "$ns->{prefix}:$attr";
167 2         5 if (LIBXML) {
168 2         7 $obj->elem->setAttributeNS($ns->{uri}, $attribute, $val);
169             } else {
170             my $ns = XML::XPath::Node::Namespace->new(
171             $ns->{prefix} => $ns->{uri}
172             );
173             $obj->elem->appendNamespace($ns);
174             $obj->elem->setAttribute($attribute => $val);
175             }
176             }
177             }
178              
179             sub get_object {
180 64     64 0 108 my $obj = shift;
181 64         140 my($ns, $name, $class) = @_;
182 64 100       159 my $ns_uri = ref($ns) eq 'XML::Atom::Namespace' ? $ns->{uri} : $ns;
183 64 100       137 my @elem = childlist($obj->elem, $ns_uri, $name) or return;
184 63         1061 my @obj = map { $class->new( Elem => $_, Namespace => $ns ) } @elem;
  84         255  
185 63 100       410 return wantarray ? @obj : $obj[0];
186             }
187              
188             sub mk_elem_accessors {
189 97     97 0 677 my $class = shift;
190 97         304 my (@list) = @_;
191 97         158 my $override_ns;
192              
193 97 100       330 if ( ref $list[-1] ) {
194 1         3 my $ns_list = pop @list;
195 1 50       6 if ( ref $ns_list eq 'ARRAY' ) {
196 1         3 $ns_list = $ns_list->[0];
197             }
198 1 50       6 if ( ref($ns_list) =~ /Namespace/ ) {
199 1         4 $override_ns = $ns_list;
200             } else {
201 0 0 0     0 if ( ref $ns_list eq 'HASH' ) {
    0          
202 0         0 $override_ns = XML::Atom::Namespace->new(%$ns_list);
203             }
204             elsif ( not ref $ns_list and $ns_list ) {
205 0         0 $override_ns = $ns_list;
206             }
207             }
208             }
209              
210 22     22   212 no strict 'refs';
  22         100  
  22         5079  
211 97         261 for my $elem ( @list ) {
212 232         474 (my $meth = $elem) =~ tr/\-/_/;
213 232         1299 *{"${class}::$meth"} = sub {
214 78     78   14875 my $obj = shift;
215 78 100       204 if (@_) {
216 25   66     117 return $obj->set( $override_ns || $obj->ns, $elem, $_[0]);
217             } else {
218 53   66     277 return $obj->get( $override_ns || $obj->ns, $elem);
219             }
220 232         962 };
221             }
222             }
223              
224             sub mk_attr_accessors {
225 62     62 0 331 my $class = shift;
226 62         261 my(@list) = @_;
227 22     22   175 no strict 'refs';
  22         65  
  22         5622  
228 62         191 for my $attr (@list) {
229 224         1726 (my $meth = $attr) =~ tr/\-/_/;
230 224         1139 *{"${class}::$meth"} = sub {
231 170     170   10226 my $obj = shift;
232 170 100       380 if (@_) {
233 45         201 return $obj->set_attr($attr => $_[0]);
234             } else {
235 125         329 return $obj->get_attr($attr);
236             }
237 224         835 };
238 224         647 $class->_add_attribute($attr);
239             }
240             }
241              
242             sub _add_attribute {
243 224     224   417 my($class, $attr) = @_;
244 224         295 push @{$class->__attributes}, $attr;
  224         631  
245             }
246              
247             sub attributes {
248 4     4 0 12 my $class = shift;
249 4         6 @{ $class->__attributes };
  4         23  
250             }
251              
252             sub mk_xml_attr_accessors {
253 59     59 0 175 my($class, @list) = @_;
254 22     22   182 no strict 'refs';
  22         67  
  22         5244  
255 59         129 for my $attr (@list) {
256 118         221 (my $meth = $attr) =~ tr/\-/_/;
257 118         525 *{"${class}::$meth"} = sub {
258 5     5   33 my $obj = shift;
259 5         8 if (LIBXML) {
260 5         14 my $elem = $obj->elem;
261 5 100       30 if (@_) {
262 1         6 $elem->setAttributeNS('http://www.w3.org/XML/1998/namespace',
263             $attr, $_[0]);
264             }
265 5         50 return $elem->getAttribute("xml:$attr");
266             } else {
267             if (@_) {
268             $obj->elem->setAttribute("xml:$attr", $_[0]);
269             }
270             return $obj->elem->getAttribute("xml:$attr");
271             }
272 118         416 };
273             }
274             }
275              
276             sub mk_object_accessor {
277 97     97 0 186 my $class = shift;
278 97         207 my($name, $ext_class) = @_;
279 22     22   175 no strict 'refs';
  22         56  
  22         4027  
280 97         200 (my $meth = $name) =~ tr/\-/_/;
281 97         535 *{"${class}::$meth"} = sub {
282 28     28   1431 my $obj = shift;
283 28   66     103 my $ns_uri = $ext_class->element_ns || $obj->ns;
284 28 100       94 if (@_) {
285 4         30 return $obj->set($ns_uri, $name, $_[0]);
286             } else {
287 24         84 return $obj->get_object($ns_uri, $name, $ext_class);
288             }
289 97         357 };
290             }
291              
292              
293             sub mk_object_list_accessor {
294 78     78 0 166 my $class = shift;
295 78         179 my($name, $ext_class, $moniker) = @_;
296              
297 22     22   177 no strict 'refs';
  22         53  
  22         12858  
298              
299 78         436 *{"$class\::$name"} = sub {
300 17     17   100 my $obj = shift;
301              
302 17   66     91 my $ns_uri = $ext_class->element_ns || $obj->ns;
303 17 50       59 if (@_) {
304             # setter: clear existent elements first
305 0         0 my @elem = childlist($obj->elem, $ns_uri, $name);
306 0         0 for my $el (@elem) {
307 0         0 $obj->elem->removeChild($el);
308             }
309              
310             # add the new elements for each
311 0         0 my $adder = "add_$name";
312 0         0 for my $add_elem (@_) {
313 0         0 $obj->$adder($add_elem);
314             }
315             } else {
316             # getter: just call get_object which is a context aware
317 17         73 return $obj->get_object($ns_uri, $name, $ext_class);
318             }
319 78         355 };
320              
321             # moniker returns always list: array ref in a scalar context
322 78 100       283 if ($moniker) {
323 76         385 *{"$class\::$moniker"} = sub {
324 5     5   591 my $obj = shift;
325 5 50       22 if (@_) {
326 0         0 return $obj->$name(@_);
327             } else {
328 5         23 my @obj = $obj->$name;
329 5 50       28 return wantarray ? @obj : \@obj;
330             }
331 76         298 };
332             }
333              
334             # add_$name
335 78         2354 *{"$class\::add_$name"} = sub {
336 10     10   800 my $obj = shift;
337 10         26 my($stuff) = @_;
338              
339 10   66     40 my $ns_uri = $ext_class->element_ns || $obj->ns;
340 10 100 66     99 my $elem = (ref $stuff && UNIVERSAL::isa($stuff, $ext_class)) ?
341             $stuff->elem : create_element($ns_uri, $name);
342 10         79 $obj->elem->appendChild($elem);
343              
344 10 100       33 if (ref($stuff) eq 'HASH') {
345 4         110 for my $k ( $ext_class->attributes ) {
346 44 100       230 defined $stuff->{$k} or next;
347 11         33 $elem->setAttribute($k, $stuff->{$k});
348             }
349             }
350 78         434 };
351             }
352              
353             sub as_xml {
354 28     28 0 6011 my $obj = shift;
355 28         51 if (LIBXML) {
356 28         176 my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
357 28         87 $doc->setDocumentElement($obj->elem->cloneNode(1));
358 28         426 return $doc->toString(1);
359             } else {
360             return '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
361             $obj->elem->toString;
362             }
363             }
364              
365             sub as_xml_utf8 {
366 1     1 0 3 my $obj = shift;
367 1         9 my $xml = $obj->as_xml;
368 1 50       114 if (utf8::is_utf8($xml)) {
369 0         0 return Encode::encode_utf8($xml);
370             }
371 1         7 return $xml;
372             }
373              
374             1;