ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/Parser.pm
Revision: 1.10
Committed: Tue Jul 24 07:56:11 2007 UTC (19 years, 2 months ago) by elmex
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +3 -3 lines
Log Message:
added sax event generator for Net::XMPP2::Node.
now other people got the serious ability to interface nicely
with Net::XMPP2 by either generating the original stanza string
and parsing it or let $node->to_sax_events () generate their
DOM tree.

File Contents

# Content
1 package Net::XMPP2::Parser;
2 use warnings;
3 use strict;
4 # OMFG!!!111 THANK YOU FOR THIS MODULE TO HANDLE THE XMPP INSANITY:
5 use Net::XMPP2::Node;
6 use XML::Parser::Expat;
7
8 =head1 NAME
9
10 Net::XMPP2::Parser - Parser for XML streams (helper for Net::XMPP2)
11
12 =head1 SYNOPSIS
13
14 use Net::XMPP2::Parser;
15 ...
16
17 =head1 DESCRIPTION
18
19 This is a XMPP XML parser helper class, which helps me to cope with the XMPP XML.
20
21 See also L<Net::XMPP2::Writer> for a discussion of the issues with XML in XMPP.
22
23 =head1 METHODS
24
25 =over 4
26
27 =item B<new>
28
29 This creates a new Net::XMPP2::Parser and calls C<init>.
30
31 =cut
32
33 sub new {
34 my $this = shift;
35 my $class = ref($this) || $this;
36 my $self = {
37 stanza_cb => sub { die "No stanza callback provided!" },
38 error_cb => sub { warn "No error callback provided: $_[0]: $_[1]!" },
39 stream_cb => sub { },
40 @_
41 };
42 bless $self, $class;
43 $self->init;
44 $self
45 }
46
47 =item B<set_stanza_cb ($cb)>
48
49 Sets the 'XML stanza' callback.
50
51 C<$cb> must be a code reference. The first argument to
52 the callback will be this Net::XMPP2::Parser instance and
53 the second will be the stanzas root Net::XMPP2::Node as first argument.
54
55 If the second argument is undefined the end of the stream has been found.
56
57 =cut
58
59 sub set_stanza_cb {
60 my ($self, $cb) = @_;
61 $self->{stanza_cb} = $cb;
62 }
63
64 =item B<set_error_cb ($cb)>
65
66 This sets the error callback that will be called when
67 the parser encounters an syntax error. The first argument
68 is the exception and the second is the data which caused the error.
69
70 =cut
71
72 sub set_error_cb {
73 my ($self, $cb) = @_;
74 $self->{error_cb} = $cb;
75 }
76
77 =item B<set_stream_cb ($cb)>
78
79 This method sets the stream tag callback. It is called
80 when the <stream> tag from the server has been encountered.
81 The first argument to the callback is the L<Net::XMPP2::Node>
82 of the opening stream tag.
83
84 =cut
85
86 sub set_stream_cb {
87 my ($self, $cb) = @_;
88 $self->{stream_cb} = $cb;
89 }
90
91 =item B<init>
92
93 This methods (re)initializes the parser.
94
95 =cut
96
97 sub init {
98 my ($self) = @_;
99 $self->{parser} = XML::Parser::ExpatNB->new (
100 Namespaces => 1,
101 ProtocolEncoding => 'UTF-8'
102 );
103 $self->{parser}->setHandlers (
104 Start => sub { $self->cb_start_tag (@_) },
105 End => sub { $self->cb_end_tag (@_) },
106 Char => sub { $self->cb_char_data (@_) },
107 );
108 $self->{nso} = {};
109 $self->{nodestack} = [];
110 }
111
112 =item B<nseq ($namespace, $tagname, $cmptag)>
113
114 This method checks whether the C<$cmptag> matches the C<$tagname>
115 in the C<$namespace>.
116
117 C<$cmptag> needs to come from the XML::Parser::Expat as it has
118 some magic attached that stores the namespace.
119
120 =cut
121
122 sub nseq {
123 my ($self, $ns, $name, $tag) = @_;
124
125 unless (exists $self->{nso}->{$ns}->{$name}) {
126 $self->{nso}->{$ns}->{$name} =
127 $self->{parser}->generate_ns_name ($name, $ns);
128 }
129
130 return $self->{parser}->eq_name ($self->{nso}->{$ns}->{$name}, $tag);
131 }
132
133 =item B<feed ($data)>
134
135 This method feeds a chunk of unparsed data to the parser.
136
137 =cut
138
139 sub feed {
140 my ($self, $data) = @_;
141 eval {
142 $self->{parser}->parse_more ($data);
143 };
144 if ($@) {
145 $self->{error_cb}->($@, $data, 'xml');
146 }
147 }
148
149 #=item B<stream_id>
150 #
151 #This method retrieves the streams ID attribute.
152 #
153 #=cut
154 #
155 #sub stream_id {
156 # my ($self) = @_;
157 # return undef unless $self->{nodestack}->[0];
158 # $self->{nodestack}->[0]->attr ('id')
159 #}
160
161 sub cb_start_tag {
162 my ($self, $p, $el, %attrs) = @_;
163 my $node = Net::XMPP2::Node->new ($p->namespace ($el), $el, \%attrs, $self);
164 $node->append_raw ($p->recognized_string);
165 if (not @{$self->{nodestack}}) {
166 $self->{stream_cb}->($node);
167 }
168 push @{$self->{nodestack}}, $node;
169 }
170
171 sub cb_char_data {
172 my ($self, $p, $str) = @_;
173 unless (@{$self->{nodestack}}) {
174 warn "characters outside of tag: [$str]!\n";
175 return;
176 }
177 my $node = $self->{nodestack}->[-1];
178 $node->add_text ($str);
179 $node->append_raw ($p->recognized_string);
180 }
181
182 sub cb_end_tag {
183 my ($self, $p, $el) = @_;
184
185 unless (@{$self->{nodestack}}) {
186 warn "end tag </$el> read without any starting tag!\n";
187 return;
188 }
189
190 if (!$p->eq_name ($self->{nodestack}->[-1]->name, $el)) {
191 warn "end tag </$el> doesn't match start tags ($self->{tags}->[-1]->[0])!\n";
192 return;
193 }
194
195 my $node = pop @{$self->{nodestack}};
196 $node->append_raw ($p->recognized_string);
197
198 # > 1 because we don't want the stream tag to save all our children...
199 if (@{$self->{nodestack}} > 1) {
200 $self->{nodestack}->[-1]->add_node ($node);
201 }
202
203 eval {
204 if (@{$self->{nodestack}} == 1) {
205 $self->{stanza_cb}->($self, $node);
206 } elsif (@{$self->{nodestack}} == 0) {
207 $self->{stanza_cb}->($self, undef);
208 }
209 };
210 if ($@) {
211 $self->{error_cb}->($@, undef, 'exception');
212 }
213 }
214
215 =back
216
217 =head1 AUTHOR
218
219 Robin Redeker, C<< <elmex at ta-sa.org> >>, JID: C<< <elmex at jabber.org> >>
220
221 =head1 COPYRIGHT & LICENSE
222
223 Copyright 2007 Robin Redeker, all rights reserved.
224
225 This program is free software; you can redistribute it and/or modify it
226 under the same terms as Perl itself.
227
228 =cut
229
230 1; # End of Net::XMPP2