1use strict;
2package XML::XBEL;
3
4use base qw (XML::XBEL::item
5	     XML::XBEL::container);
6
7# $Id: XBEL.pm,v 1.9 2005/04/02 20:54:52 asc Exp $
8
9=head1 NAME
10
11XML::XBEL - OOP for reading and writing XBEL documents.
12
13=head1 SYNOPSIS
14
15 # creating an XBEL document
16
17 use XML::XBEL;
18 use XML::XBEL::Folder;
19 use XML::XBEL::Bookmark;
20
21 my $xbel = XML::XBEL->new();
22 $xbel->new_document({title=>"My Bookmarks"});
23
24 $xbel->add_bookmark({href  => "http://foo.com",
25	 	      title => "foo",
26		      desc  => "bar"});
27
28 my $folder1 = XML::XBEL::Folder->new({title => "comp"});
29 my $folder2 = XML::XBEL::Folder->new({title => "lang"});
30 my $folder3 = XML::XBEL::Folder->new({title => "perl"});
31
32 my $bm = XML::XBEL::Bookmark->new({"title=>"misc"});
33 $bm->href("http://groups.google.com/groups?q=comp.lang.perl.misc");
34
35 $folder3->add_bookmark($bm);
36 $folder2->add_folder($folder3);
37 $folder1->add_folder($folder2);
38
39 $xbel->add_folder($folder1);
40
41 print $xbel->toString();
42
43 # parsing an XBEL document
44
45 use XML::XBEL;
46
47 my $xbel = XML::XBEL->new();
48 $xbel->parse_file($file);
49
50 foreach my $bm ($xbel->bookmarks()) {
51
52     print sprintf("%s points to %s\n",
53		   $bm->title(),
54		   $bm->href());
55 }
56
57=head1 DESCRIPTION
58
59OOP for reading and writing XBEL files.
60
61=cut
62
63$XML::XBEL::VERSION = '1.4';
64
65use XML::LibXML;
66
67=head1 PACKAGE METHODS
68
69=cut
70
71=head2 __PACKAGE__->new()
72
73Returns an I<XML::XBEL> object.
74
75=cut
76
77sub new {
78    my $pkg  = shift;
79
80    return bless {'__doc'  => undef,
81		  '__root' => undef } , $pkg;
82}
83
84=head1 OBJECT METHODS
85
86=cut
87
88=head2 $self->parse_file($file)
89
90Returns true or false.
91
92=cut
93
94sub parse_file {
95    my $self = shift;
96    my $file = shift;
97
98    my $parser = XML::LibXML->new();
99    my $doc = $parser->parse_file($file);
100
101    return $self->_parse($doc);
102}
103
104=head2 $self->parse_string($string)
105
106Returns true or false.
107
108=cut
109
110sub parse_string {
111    my $self = shift;
112    my $str  = shift;
113
114    my $parser = XML::LibXML->new();
115    my $doc = $parser->parse_string($str);
116
117    return $self->_parse($doc);
118}
119
120sub _parse {
121    my $self = shift;
122    my $doc  = shift;
123
124    $self->{'__doc'}  = $doc;
125    $self->{'__root'} = $doc->documentElement();
126
127    return 1;
128}
129
130=head2 $obj->new_document(\%args)
131
132Valid arguments are :
133
134=over 4
135
136=item * B<title>
137
138String.
139
140=item * B<desc>
141
142String.
143
144=item * B<info>
145
146Hash ref, with the following key/value pairs :
147
148=over 6
149
150=item * I<owner>
151
152Array ref.
153
154=back
155
156=back
157
158Returns true or false.
159
160=cut
161
162sub new_document {
163    my $self = shift;
164    my $args = shift;
165
166    my $doc = XML::LibXML::Document->new();
167
168    if ($args->{encoding}) {
169	$doc->setEncoding($args->{encoding});
170    }
171
172    my $root = XML::LibXML::Element->new("xbel");
173    $doc->setDocumentElement($root);
174
175    $self->{'__doc'}  = $doc;
176    $self->{'__root'} = $root;
177
178    foreach my $el ("title","desc","info") {
179
180	if (! exists($args->{$el})) {
181	    next;
182	}
183
184	$self->$el($args->{$el});
185    }
186
187    return 1;
188}
189
190=head2 $obj->title($title)
191
192Get/set the title for an XBEL document.
193
194Returns a string when called with no arguments;
195otherwise returns true or false.
196
197=cut
198
199# Defined in XML::XBEL::item
200
201=head2 $obj->desc($description)
202
203Get/set the description for an XBEL document.
204
205Returns a string when called with no arguments;
206otherwise returns true or false.
207
208=cut
209
210# Defined in XML::XBEL::item
211
212=head2 $obj->info(\%args)
213
214Get/set the metadata for an XBEL document.
215
216Valid args are :
217
218=over 4
219
220=item * B<owner>
221
222Array reference
223
224=back
225
226Returns an array reference when called with no arguments;
227otherwise returns true or false.
228
229=cut
230
231# Defined in XML::XBEL::info
232
233=head2 $obj->bookmarks($recursive)
234
235Returns a list of child I<XML::XBEL::Bookmark> objects.
236
237Where I<$recursive> is a boolean indicating whether to
238return all the bookmarks in an XBEL document or only its
239immediate children.
240
241=cut
242
243# Defined in XML::XBEL::container
244
245=head2 $obj->folders($recursive)
246
247Returns a list of child I<XML::XBEL::Folder> objects.
248
249Where I<$recursive> is a boolean indicating whether to
250return all the folders in an XBEL document or only its
251immediate children.
252
253=cut
254
255# Defined in XML::XBEL::container
256
257=head2 $obj->aliases($recursive)
258
259Returns a list of child I<XML::XBEL::Alias> objects.
260
261Where I<$recursive> is a boolean indicating whether to
262return all the aliases in an XBEL document or only its
263immediate children.
264
265=cut
266
267# Defined in XML::XBEL::container
268
269=head2 $obj->find_by_id($id)
270
271Returns an I<XML::XBEL::Bookmark> or I<XML::XBEL::Folder>
272object whose id attribute matches $id.
273
274=cut
275
276sub find_by_id {
277    my $self = shift;
278    my $id   = shift;
279
280    my $node = ($self->{'__root'}->findnodes("//child::*[\@id='$id']"))[0];
281
282    # print sprintf("%s %s\n",$node,$node->nodeName());
283
284    if (! $node) {
285	return undef;
286    }
287
288    elsif ($node->nodeName() eq "folder") {
289	require XML::XBEL::Folder;
290	return XML::XBEL::Folder->build_node($node);
291    }
292
293    elsif ($node->nodeName() eq "bookmark") {
294	require XML::XBEL::Bookmark;
295	return XML::XBEL::Bookmark->build_node($node);
296    }
297
298    else {
299	return undef;
300    }
301}
302
303=head2 $obj->find_by_href($href)
304
305Returns a list of I<XML::XBEL::Bookmark> objects whose
306href attribute matches $href.
307
308=cut
309
310sub find_by_href {
311    my $self = shift;
312    my $href = shift;
313
314    my @nodes = $self->{'__root'}->findnodes("//child::*[name()='bookmark' and \@href='$href']");
315
316    if (! @nodes) {
317	return undef;
318    }
319
320    require XML::XBEL::Bookmark;
321
322    return map {
323	XML::XBEL::Bookmark->build_node($_);
324    } @nodes
325}
326
327=head2 $obj->add_bookmark((XML::XBEL::Bookmark || \%args))
328
329Add a new bookmark to an XBEL document.
330
331If passed a hash ref, valid arguments are the same as those
332defined for the I<XML::XBEL::Bookmark> object constructor.
333
334=cut
335
336# Defined in XML::XBEL::container
337
338=head2 $obj->add_folder((XML::XBEL::Folder || \%args))
339
340Add a new folder to an XBEL document.
341
342If passed a hash ref, valid arguments are the same as those
343defined for the I<XML::XBEL::Folder> object constructor.
344
345=cut
346
347# Defined in XML::XBEL::container
348
349=head2 $obj->add_alias((XML::XBEL::Alias || \%args))
350
351Add a new alias to an XBEL document.
352
353If passed a hash ref, valid arguments are the same as those
354defined for the I<XML::XBEL::Alias> object constructor.
355
356=cut
357
358# Defined in XML::XBEL::container
359
360=head2 $obj->add_separator()
361
362Add a new separator to an XBEL document.
363
364=cut
365
366# Defined in XML::XBEL::container
367
368=head2 $obj->toString($format)
369
370=cut
371
372sub toString {
373    my $self = shift;
374    $self->{'__doc'}->toString(@_);
375}
376
377=head2 $obj->toFile($filename,$format)
378
379=cut
380
381sub toFile {
382    my $self = shift;
383    $self->{'__doc'}->toString(@_);
384}
385
386=head2 $obj->toFH(\*$fh,$format)
387
388=cut
389
390sub toFH {
391    my $self = shift;
392    $self->{'__doc'}->toString(@_);
393}
394
395=head2 $obj->toSAX(A::SAX::Handler)
396
397Generate SAX events for the XBEL object.
398
399=cut
400
401sub toSAX {
402    my $self    = shift;
403    my $handler = shift;
404
405    require XML::LibXML::SAX::Parser;
406    my $gen = XML::LibXML::SAX::Parser->new(Handler => $handler);
407    $gen->generate($self->{'__doc'});
408}
409
410=head1 VERSION
411
4121.4
413
414=head1 DATE
415
416$Date: 2005/04/02 20:54:52 $
417
418=head1 AUTHOR
419
420Aaron Straup Cope E<lt>ascope@cpan.orgE<gt>
421
422=head1 SEE ALSO
423
424L<XML::XBEL::Folder>
425
426L<XML::XBEL::Bookmark>
427
428L<XML::XBEL::Alias>
429
430L<XML::XBEL::Separator>
431
432=head1 BUGS
433
434It's possible. Please report all bugs via http://rt.cpan.org
435
436=head1 LICENSE
437
438Copyright (c) 2004 Aaron Straup Cope. All rights reserved.
439
440This is free software, you may use it and distribute it under the
441same terms as Perl itself.
442
443=cut
444
445return 1;
446