File Coverage

blib/lib/XML/Atom/Base.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             # $Id$
2              
3             package XML::Atom::Base;
4 13     13   580 use strict;
  13         19  
  13         805  
5 13     13   63 use base qw( XML::Atom::ErrorHandler Class::Data::Inheritable );
  13         24  
  13         6649  
6              
7 13     13   30725 use Encode;
  13         194278  
  13         1380  
8 13     13   6437 use XML::Atom;
  0            
  0            
9             use XML::Atom::Util qw( set_ns first nodelist childlist create_element );
10              
11             __PACKAGE__->mk_classdata('__attributes', []);
12              
13             sub new {
14             my $class = shift;
15             my $obj = bless {}, $class;
16             $obj->init(@_) or return $class->error($obj->errstr);
17             $obj;
18             }
19              
20             sub init {
21             my $obj = shift;
22             my %param = @_;
23             if (!exists $param{Namespace} and my $ns = $obj->element_ns) {
24             $param{Namespace} = $ns;
25             }
26             $obj->set_ns(\%param);
27             my $elem;
28             unless ($elem = $param{Elem}) {
29             if (LIBXML) {
30             my $doc = XML::LibXML::Document->createDocument('1.0', 'UTF-8');
31             my $ns = $obj->ns;
32             my ($ns_uri, $ns_prefix);
33             if ( ref $ns and $ns->isa('XML::Atom::Namespace') ) {
34             $ns_uri = $ns->{uri};
35             $ns_prefix = $ns->{prefix};
36             } else {
37             $ns_uri = $ns;
38             }
39             if ( $ns_uri and $ns_prefix ) {
40             $elem = $doc->createElement($obj->element_name);
41             $elem->setNamespace( $ns_uri, $ns_prefix, 1 );
42             } else {
43             $elem = $doc->createElementNS($obj->ns, $obj->element_name);
44             }
45             $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             $obj->{elem} = $elem;
53             $obj;
54             }
55              
56             sub element_name { }
57             sub element_ns { }
58              
59             sub ns { $_[0]->{ns} }
60             sub elem { $_[0]->{elem} }
61              
62             sub version {
63             my $atom = shift;
64             XML::Atom::Util::ns_to_version($atom->ns);
65             }
66              
67             sub content_type {
68             my $atom = shift;
69             if ($atom->version >= 1.0) {
70             return "application/atom+xml";
71             } else {
72             return "application/x.atom+xml";
73             }
74             }
75              
76             sub get {
77             my $obj = shift;
78             my($ns, $name) = @_;
79             my @list = $obj->getlist($ns, $name);
80             return $list[0];
81             }
82              
83             sub getlist {
84             my $obj = shift;
85             my($ns, $name) = @_;
86             my $ns_uri = ref($ns) eq 'XML::Atom::Namespace' ? $ns->{uri} : $ns;
87             my @node = childlist($obj->elem, $ns_uri, $name);
88             return map {
89             my $val = LIBXML ? $_->textContent : $_->string_value;
90             if ($] >= 5.008) {
91             require Encode;
92             Encode::_utf8_off($val) unless $XML::Atom::ForceUnicode;
93             }
94             $val;
95             } @node;
96             }
97              
98             sub add {
99             my $obj = shift;
100             my($ns, $name, $val, $attr) = @_;
101             return $obj->set($ns, $name, $val, $attr, 1);
102             }
103              
104             sub set {
105             my $obj = shift;
106             my($ns, $name, $val, $attr, $add) = @_;
107             my $ns_uri = ref $ns eq 'XML::Atom::Namespace' ? $ns->{uri} : $ns;
108             my @elem = childlist($obj->elem, $ns_uri, $name);
109             if (!$add && @elem) {
110             $obj->elem->removeChild($_) for @elem;
111             }
112             my $elem = create_element($ns, $name);
113             if (UNIVERSAL::isa($val, 'XML::Atom::Base')) {
114             if (LIBXML) {
115             for my $child ($val->elem->childNodes) {
116             $elem->appendChild($child->cloneNode(1));
117             }
118             for my $attr ($val->elem->attributes) {
119             next unless ref($attr) eq 'XML::LibXML::Attr';
120             $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             if (LIBXML) {
134             $elem->appendChild(XML::LibXML::Text->new($val));
135             } else {
136             $elem->appendChild(XML::XPath::Node::Text->new($val));
137             }
138             }
139             $obj->elem->appendChild($elem);
140             if ($attr) {
141             while (my($k, $v) = each %$attr) {
142             $elem->setAttribute($k, $v);
143             }
144             }
145             return $val;
146             }
147              
148             sub get_attr {
149             my $obj = shift;
150             my($attr) = @_;
151             my $val = $obj->elem->getAttribute($attr);
152             if ($] >= 5.008) {
153             require Encode;
154             Encode::_utf8_off($val) unless $XML::Atom::ForceUnicode;
155             }
156             $val;
157             }
158              
159             sub set_attr {
160             my $obj = shift;
161             if (@_ == 2) {
162             my($attr, $val) = @_;
163             $obj->elem->setAttribute($attr => $val);
164             } elsif (@_ == 3) {
165             my($ns, $attr, $val) = @_;
166             my $attribute = "$ns->{prefix}:$attr";
167             if (LIBXML) {
168             $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             my $obj = shift;
181             my($ns, $name, $class) = @_;
182             my $ns_uri = ref($ns) eq 'XML::Atom::Namespace' ? $ns->{uri} : $ns;
183             my @elem = childlist($obj->elem, $ns_uri, $name) or return;
184             my @obj = map { $class->new( Elem => $_, Namespace => $ns ) } @elem;
185             return wantarray ? @obj : $obj[0];
186             }
187              
188             sub mk_elem_accessors {
189             my $class = shift;
190             my (@list) = @_;
191             my $override_ns;
192              
193             if ( ref $list[-1] ) {
194             my $ns_list = pop @list;
195             if ( ref $ns_list eq 'ARRAY' ) {
196             $ns_list = $ns_list->[0];
197             }
198             if ( ref($ns_list) =~ /Namespace/ ) {
199             $override_ns = $ns_list;
200             } else {
201             if ( ref $ns_list eq 'HASH' ) {
202             $override_ns = XML::Atom::Namespace->new(%$ns_list);
203             }
204             elsif ( not ref $ns_list and $ns_list ) {
205             $override_ns = $ns_list;
206             }
207             }
208             }
209              
210             no strict 'refs';
211             for my $elem ( @list ) {
212             (my $meth = $elem) =~ tr/\-/_/;
213             *{"${class}::$meth"} = sub {
214             my $obj = shift;
215             if (@_) {
216             return $obj->set( $override_ns || $obj->ns, $elem, $_[0]);
217             } else {
218             return $obj->get( $override_ns || $obj->ns, $elem);
219             }
220             };
221             }
222             }
223              
224             sub mk_attr_accessors {
225             my $class = shift;
226             my(@list) = @_;
227             no strict 'refs';
228             for my $attr (@list) {
229             (my $meth = $attr) =~ tr/\-/_/;
230             *{"${class}::$meth"} = sub {
231             my $obj = shift;
232             if (@_) {
233             return $obj->set_attr($attr => $_[0]);
234             } else {
235             return $obj->get_attr($attr);
236             }
237             };
238             $class->_add_attribute($attr);
239             }
240             }
241              
242             sub _add_attribute {
243             my($class, $attr) = @_;
244             push @{$class->__attributes}, $attr;
245             }
246              
247             sub attributes {
248             my $class = shift;
249             @{ $class->__attributes };
250             }
251              
252             sub mk_xml_attr_accessors {
253             my($class, @list) = @_;
254             no strict 'refs';
255             for my $attr (@list) {
256             (my $meth = $attr) =~ tr/\-/_/;
257             *{"${class}::$meth"} = sub {
258             my $obj = shift;
259             if (LIBXML) {
260             my $elem = $obj->elem;
261             if (@_) {
262             $elem->setAttributeNS('http://www.w3.org/XML/1998/namespace',
263             $attr, $_[0]);
264             }
265             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             };
273             }
274             }
275              
276             sub mk_object_accessor {
277             my $class = shift;
278             my($name, $ext_class) = @_;
279             no strict 'refs';
280             (my $meth = $name) =~ tr/\-/_/;
281             *{"${class}::$meth"} = sub {
282             my $obj = shift;
283             my $ns_uri = $ext_class->element_ns || $obj->ns;
284             if (@_) {
285             return $obj->set($ns_uri, $name, $_[0]);
286             } else {
287             return $obj->get_object($ns_uri, $name, $ext_class);
288             }
289             };
290             }
291              
292              
293             sub mk_object_list_accessor {
294             my $class = shift;
295             my($name, $ext_class, $moniker) = @_;
296              
297             no strict 'refs';
298              
299             *{"$class\::$name"} = sub {
300             my $obj = shift;
301              
302             my $ns_uri = $ext_class->element_ns || $obj->ns;
303             if (@_) {
304             # setter: clear existent elements first
305             my @elem = childlist($obj->elem, $ns_uri, $name);
306             for my $el (@elem) {
307             $obj->elem->removeChild($el);
308             }
309              
310             # add the new elements for each
311             my $adder = "add_$name";
312             for my $add_elem (@_) {
313             $obj->$adder($add_elem);
314             }
315             } else {
316             # getter: just call get_object which is a context aware
317             return $obj->get_object($ns_uri, $name, $ext_class);
318             }
319             };
320              
321             # moniker returns always list: array ref in a scalar context
322             if ($moniker) {
323             *{"$class\::$moniker"} = sub {
324             my $obj = shift;
325             if (@_) {
326             return $obj->$name(@_);
327             } else {
328             my @obj = $obj->$name;
329             return wantarray ? @obj : \@obj;
330             }
331             };
332             }
333              
334             # add_$name
335             *{"$class\::add_$name"} = sub {
336             my $obj = shift;
337             my($stuff) = @_;
338              
339             my $ns_uri = $ext_class->element_ns || $obj->ns;
340             my $elem = (ref $stuff && UNIVERSAL::isa($stuff, $ext_class)) ?
341             $stuff->elem : create_element($ns_uri, $name);
342             $obj->elem->appendChild($elem);
343              
344             if (ref($stuff) eq 'HASH') {
345             for my $k ( $ext_class->attributes ) {
346             defined $stuff->{$k} or next;
347             $elem->setAttribute($k, $stuff->{$k});
348             }
349             }
350             };
351             }
352              
353             sub as_xml {
354             my $obj = shift;
355             if (LIBXML) {
356             my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
357             $doc->setDocumentElement($obj->elem->cloneNode(1));
358             return $doc->toString(1);
359             } else {
360             return '' . "\n" .
361             $obj->elem->toString;
362             }
363             }
364              
365             sub as_xml_utf8 {
366             my $obj = shift;
367             my $xml = $obj->as_xml;
368             if (utf8::is_utf8($xml)) {
369             return Encode::encode_utf8($xml);
370             }
371             return $xml;
372             }
373              
374             1;