File Coverage

blib/lib/AnyEvent/XMPP/Node.pm
Criterion Covered Total %
statement 12 83 14.4
branch 0 22 0.0
condition 0 6 0.0
subroutine 4 20 20.0
pod 15 15 100.0
total 31 146 21.2


line stmt bran cond sub pod time code
1             package AnyEvent::XMPP::Node;
2 21     21   116 use strict;
  21         38  
  21         825  
3 21     21   10887 use AnyEvent::XMPP::Namespaces qw/xmpp_ns/;
  21         55  
  21         2277  
4              
5             use constant {
6 21         4048 NS => 0,
7             NAME => 1,
8             ATTRS => 2,
9             TEXT => 3,
10             NODES => 4,
11             PARSER => 5,
12             RAW => 6
13 21     21   122 };
  21         38  
14              
15             use constant {
16 21         37037 NNODE => 0,
17             NTEXT => 1,
18             NRAW => 2,
19 21     21   128 };
  21         39  
20              
21             =head1 NAME
22              
23             AnyEvent::XMPP::Node - XML node tree helper for the parser.
24              
25             =head1 SYNOPSIS
26              
27             use AnyEvent::XMPP::Node;
28             ...
29              
30             =head1 DESCRIPTION
31              
32             This class represens a XML node. L should usually not
33             require messing with the parse tree, but sometimes it is neccessary.
34              
35             If you experience any need for messing with these and feel L should
36             rather take care of it drop me a mail, feature request or most preferably a patch!
37              
38             Every L has a namespace, attributes, text and child nodes.
39              
40             You can access these with the following methods:
41              
42             =head1 METHODS
43              
44             =over 4
45              
46             =item B
47              
48             Creates a new AnyEvent::XMPP::Node object with the node tag name C<$el> in the
49             namespace URI C<$ns> and the attributes C<$attrs>. The C<$parser> must be
50             the instance of C which generated this node.
51              
52             =cut
53              
54             sub new {
55 0     0 1   my $this = shift;
56 0   0       my $class = ref($this) || $this;
57 0           my $self = [];
58 0           $self->[0] = $_[0];
59 0           $self->[1] = $_[1];
60 0           $self->[2] = $_[2];
61 0           $self->[5] = $_[3];
62 0           $self->[6] = '';
63 0           bless $self, $class;
64 0           return $self
65             }
66              
67             =item B
68              
69             The tag name of this node.
70              
71             =cut
72              
73             sub name {
74 0     0 1   $_[0]->[NAME]
75             }
76              
77             =item B
78              
79             Returns the namespace URI of this node.
80              
81             =cut
82              
83             sub namespace {
84 0     0 1   $_[0]->[NS]
85             }
86              
87             =item B
88              
89             Returns true whether the current element matches the tag name C<$name>
90             in the namespaces pointed at by C<$namespace_or_alias>.
91              
92             You can either pass an alias that was defined in L
93             or pass an namespace URI in C<$namespace_or_alias>. If no alias with the name
94             C<$namespace_or_alias> was found in L it will be
95             interpreted as namespace URI.
96              
97             The first argument to eq can also be another L instance.
98              
99             =cut
100              
101             sub eq {
102 0     0 1   my ($self, $n, $name) = @_;
103 0 0         if (ref $n) {
104 0           return $self->[PARSER]->nseq ($n->namespace, $n->name, $self->name);
105             } else {
106 0           my $ns = xmpp_ns ($n);
107 0 0         return $self->[PARSER]->nseq (($ns ? $ns : $n), $name, $self->name);
108             }
109             }
110              
111             =item B
112              
113             This method return true if the namespace of this instance of L
114             matches the namespace described by C<$namespace_or_alias> or the
115             namespace of the C<$node> which has to be another L instance.
116              
117             See C for the meaning of C<$namespace_or_alias>.
118              
119             =cut
120              
121             sub eq_ns {
122 0     0 1   my ($self, $n) = @_;
123 0 0         if (ref $n) {
124 0           return ($n->namespace eq $self->namespace);
125             } else {
126 0           my $ns = xmpp_ns ($n);
127 0   0       $ns ||= $n;
128 0           return ($ns eq $self->namespace);
129             }
130             }
131              
132             =item B
133              
134             Returns the contents of the C<$name> attribute.
135              
136             =cut
137              
138             sub attr {
139 0     0 1   $_[0]->[ATTRS]->{$_[1]};
140             }
141              
142             =item B
143              
144             Adds a sub-node to the current node.
145              
146             =cut
147              
148             sub add_node {
149 0     0 1   my ($self, $node) = @_;
150 0           push @{$self->[NODES]}, [NNODE, $node];
  0            
151             }
152              
153             =item B
154              
155             Returns a list of sub nodes.
156              
157             =cut
158              
159             sub nodes {
160 0           map { $_->[1] }
  0            
161 0 0         grep { $_->[0] == NNODE }
162 0     0 1   @{$_[0]->[NODES] || []};
163             }
164              
165             =item B
166              
167             Adds character data to the current node.
168              
169             =cut
170              
171             sub add_text {
172 0     0 1   my ($self, $text) = @_;
173 0           push @{$self->[NODES]}, [NTEXT, $text];
  0            
174             }
175              
176             =item B
177              
178             Returns the text for this node.
179              
180             =cut
181              
182             sub text {
183 0 0   0 1   join '', map $_->[1], grep { $_->[0] == NTEXT } @{$_[0]->[NODES] || []}
  0            
  0            
184             }
185              
186             =item B
187              
188             This method does a recursive descent through the sub-nodes and
189             fetches all nodes that match the last element of C<@path>.
190              
191             The elements of C<@path> consist of a array reference to an array with
192             two elements: the namespace key known by the C<$parser> and the tagname
193             we search for.
194              
195             =cut
196              
197             sub find_all {
198 0     0 1   my ($self, @path) = @_;
199 0           my $cur = shift @path;
200 0           my @ret;
201 0           for my $n ($self->nodes) {
202 0 0         if ($n->eq (@$cur)) {
203 0 0         if (@path) {
204 0           push @ret, $n->find_all (@path);
205             } else {
206 0           push @ret, $n;
207             }
208             }
209             }
210             @ret
211 0           }
212              
213             =item B
214              
215             This writes the current node out to the L object in C<$writer>.
216              
217             =cut
218              
219             sub write_on {
220 0     0 1   my ($self, $w) = @_;
221 0           $w->raw ($self->as_string);
222             }
223              
224              
225             =item B
226              
227             This method returns the original character representation of this XML element
228             (and it's children nodes). Please note that the string is a unicode string,
229             meaning: to get octets use:
230              
231             my $octets = encode ('UTF-8', $node->as_string);
232              
233             Now you can roll stunts like this:
234              
235             my $libxml = XML::LibXML->new;
236             my $doc = $libxml->parse_string (encode ('UTF-8', $node->as_string ()));
237              
238             (You can use your favorite XML parser :)
239              
240             =cut
241              
242             sub as_string {
243 0     0 1   my ($self) = @_;
244 0 0         join '',
245 0           map { $_->[0] == NRAW ? $_->[1] : $_->[1]->as_string }
246 0 0         grep { $_->[0] != NTEXT }
247 0           @{$self->[NODES] || []};
248             }
249              
250             =item B
251              
252             This method is called by the parser to store original strings of this element.
253              
254             =cut
255              
256             sub append_raw {
257 0     0 1   my ($self, $str) = @_;
258 0           push @{$self->[NODES]}, [NRAW, $str];
  0            
259             }
260              
261             =item B
262              
263             This method takes anything that can receive SAX events.
264             See also L or L
265             or L.
266              
267             With this you can convert this node to any DOM level 2 structure you want:
268              
269             my $builder = XML::LibXML::SAX::Builder->new;
270             $node->to_sax_events ($builder);
271             my $dom = $builder->result;
272             print "Canonized: " . $dom->toStringC14N . "\n";
273              
274             =cut
275              
276             sub to_sax_events {
277 0     0 1   my ($self, $handler) = @_;
278 0           my $doc = { Parent => undef };
279 0           $handler->start_document ($doc);
280 0           $self->_to_sax_events ($handler);
281 0           $handler->end_document ($doc);
282             }
283              
284             sub _to_sax_events {
285 0     0     my ($self, $handler) = @_;
286 0           $handler->start_element ({
287             NamespaceURI => $self->namespace,
288             Name => $self->name,
289             Attributes => {
290             map {
291 0           ($_ => { Name => $_, Value => $self->[ATTRS]->{$_} })
292 0           } keys %{$self->[ATTRS]}
293             }
294             });
295 0           for (@{$self->[NODES]}) {
  0            
296 0 0         if ($_->[0] == NTEXT) {
    0          
297 0           $handler->characters ($_->[1]);
298             } elsif ($_->[0] == NNODE) {
299 0           $_->[1]->_to_sax_events ($handler);
300             }
301             }
302             $handler->end_element ({
303 0           NamespaceURI => $self->namespace,
304             Name => $self->name,
305             });
306             }
307              
308             =back
309              
310             =head1 AUTHOR
311              
312             Robin Redeker, C<< >>, JID: C<< >>
313              
314             =head1 COPYRIGHT & LICENSE
315              
316             Copyright 2007, 2008 Robin Redeker, all rights reserved.
317              
318             This program is free software; you can redistribute it and/or modify it
319             under the same terms as Perl itself.
320              
321             =cut
322              
323             1; # End of AnyEvent::XMPP