File Coverage

blib/lib/XML/Generator/vCard.pm
Criterion Covered Total %
statement 252 332 75.9
branch 40 88 45.4
condition 4 13 30.7
subroutine 43 45 95.5
pod 5 5 100.0
total 344 483 71.2


line stmt bran cond sub pod time code
1             # $Id: vCard.pm,v 1.28 2004/12/28 23:31:29 asc Exp $
2 2     2   3553 use strict;
  2         5  
  2         126  
3              
4             package XML::Generator::vCard;
5 2     2   14 use base qw (XML::Generator::vCard::Base);
  2         4  
  2         3277  
6              
7             $XML::Generator::vCard::VERSION = '1.3';
8              
9             =head1 NAME
10              
11             XML::Generator::vCard - generate SAX2 events for vCard 3.0
12              
13             =head1 SYNOPSIS
14              
15             use XML::SAX::Writer;
16             use XML::Generator::vCard;
17              
18             my $writer = XML::SAX::Writer->new();
19             my $driver = XML::Generator::vCard->new(Handler=>$writer);
20              
21             $driver->parse_files("test.vcf");
22              
23             =head1 DESCRIPTION
24              
25             Generate SAX2 events for vCard 3.0.
26              
27             This package supersedes I.
28              
29             =head1 DOCUMENT FORMAT
30              
31             SAX2 events map to the I draft:
32              
33             http://xml.coverpages.org/draft-dawson-vcard-xml-dtd-00.txt
34              
35             The draft itself has since expired but it still seems like a
36             perfectly good place to start from.
37              
38             =cut
39              
40 2     2   180467 use Encode;
  2         5  
  2         454  
41 2     2   3321 use MIME::Base64;
  2         2861  
  2         170  
42 2     2   4349 use Text::vCard::Addressbook;
  2         196450  
  2         43  
43              
44 2         192 use constant NS => {"vCard" => "x-urn:cpan:ascope:xml-generator-vcard#",
45 2     2   125 "foaf" => "http://xmlns.com/foaf/0.1/"};
  2         5  
46              
47 2     2   11 use constant VCARD_VERSION => "3.0";
  2         7  
  2         87  
48 2     2   11 use constant VCARD_CLASS => "PUBLIC";
  2         5  
  2         279  
49              
50             sub import {
51 2     2   46 my $pkg = shift;
52 2         30 $pkg->SUPER::import(@_);
53              
54 2         9500 my $ns = $pkg->namespaces();
55 2         15 $ns->{ vCard } = "x-urn:cpan:ascope:xml-generator-vcard#";
56            
57 2     2   28 no strict "refs";
  2         4  
  2         11134  
58 2     2   10 * { join("::",$pkg,"namespaces") } = sub { return $ns; };
  2         12  
  2         8  
59              
60 2         32 return 1;
61             }
62              
63             =head1 PACKAGE METHODS
64              
65             =cut
66              
67             =head2 __PACKAGE__->new(%args)
68              
69             This method inherits from I.
70              
71             Returns a I object.
72              
73             =cut
74              
75             =head1 OBJECT METHODS
76              
77             =cut
78              
79             =head2 $pkg->parse_files(@files)
80              
81             Generate SAX2 events for one, or more, vCard files.
82              
83             Returns true or false.
84              
85             =cut
86              
87             sub parse_files {
88 1     1 1 2043 my $self = shift;
89 1         4 my @files = @_;
90              
91 1         3 my $book = ();
92            
93 1         3 eval {
94 1         22 $book = Text::vCard::Addressbook->load(\@files);
95             };
96              
97 1 50       14252 if ($@) {
98 0         0 warn $@;
99 0         0 return 0;
100             }
101              
102 1         7 return $self->_render_doc([ $book->vcards() ]);
103             }
104              
105             =head1 PRIVATE METHODS
106              
107             Private methods are documented below in case you need to subclass
108             this package to tweak its output.
109              
110             =cut
111              
112             =head2 $obj->_render_doc(\@vcards)
113              
114             =cut
115              
116             sub _render_doc {
117 1     1   11 my $self = shift;
118 1         3 my $cards = shift;
119              
120 1         153 $self->start_document();
121              
122 1 50       4 if (scalar(@$cards) > 1) {
123            
124 0         0 $self->start_element({Name => "vCard:vCardSet"});
125            
126 0         0 foreach my $vcard (@$cards) {
127 0         0 $self->_render_card($vcard);
128             }
129            
130 0         0 $self->end_element({Name => "vCard:vCardSet"});
131             }
132            
133             else {
134 1         8 $self->_render_card($cards->[0]);
135             }
136            
137             #
138            
139 1         4 $self->end_document();
140 1         32 return 1;
141             }
142              
143             =head2 $obj->_render_card(Text::vCard)
144              
145             =cut
146              
147             sub _render_card {
148 1     1   2 my $self = shift;
149 1         3 my $vcard = shift;
150            
151 1   50     29 my $attrs = {
      50        
152             "{}version" => {Name => "vCard:version",
153             Value => ($vcard->version() || VCARD_VERSION)},
154             "{}class" => {Name => "vCard:class",
155             Value => ($vcard->class() || VCARD_CLASS)},
156             };
157              
158             #
159              
160 1         12363 foreach my $prop ("uid","rev","prodid") {
161 3 50       1333 if (my $value = $vcard->$prop()) {
162 0         0 $attrs->{"{}$prop"} = {Name => "vCard:$prop",
163             Value => $value};
164             }
165             }
166              
167 1         31 $self->start_element({Name => "vCard:vCard",
168             Attributes => $attrs});
169              
170             #
171              
172 1         48 $self->_render_fn($vcard);
173 1         5 $self->_render_n($vcard);
174 1         5 $self->_render_nickname($vcard);
175 1         6 $self->_render_photo($vcard);
176 1         5 $self->_render_bday($vcard);
177 1         4 $self->_render_adrs($vcard);
178 1         6 $self->_render_labels($vcard);
179 1         5 $self->_render_tels($vcard);
180 1         4 $self->_render_emails($vcard);
181 1         5 $self->_render_instantmessaging($vcard);
182 1         4 $self->_render_mailer($vcard);
183 1         4 $self->_render_tz($vcard);
184 1         5 $self->_render_geo($vcard);
185 1         5 $self->_render_org($vcard);
186 1         3 $self->_render_title($vcard);
187 1         4 $self->_render_role($vcard);
188 1         4 $self->_render_logo($vcard);
189             # AGENT
190 1         4 $self->_render_categories($vcard);
191 1         3 $self->_render_note($vcard);
192             # SORT
193 1         4 $self->_render_sound($vcard);
194 1         3 $self->_render_url($vcard);
195 1         4 $self->_render_key($vcard);
196 1         4 $self->_render_custom($vcard);
197              
198 1         4 $self->end_element({Name=>"vCard:vCard"});
199              
200 1         9 return 1;
201             }
202              
203             =head2 $obj->_render_fn(Text::vCard)
204              
205             =cut
206              
207             sub _render_fn {
208 1     1   2 my $self = shift;
209 1         3 my $vcard = shift;
210              
211 1         7 $self->_pcdata({Name => "vCard:fn",
212             Value => $vcard->fn()});
213            
214 1         3 return 1;
215             }
216              
217             =head2 $obj->_render_n(Text::vCard)
218              
219             =cut
220              
221             sub _render_n {
222 1     1   3 my $self = shift;
223 1         2 my $vcard = shift;
224              
225 1         7 my $n = $vcard->get({"node_type" => "name"});
226              
227 1 50       755 if (! $n) {
228 0         0 return 1;
229             }
230              
231 1         4 $n = $n->[0];
232              
233             #
234              
235 1 50 33     16 if (($n->family()) || ($n->given())) {
236              
237 0         0 $self->start_element({Name=>"vCard:n"});
238            
239 0         0 $self->_pcdata({Name => "vCard:family",
240             Value => $n->family()});
241            
242 0         0 $self->_pcdata({Name => "vCard:given",
243             Value => $n->given()});
244            
245 0 0       0 if (my $o = $n->middle()) {
246 0         0 $self->_pcdata({Name => "vCard:other",
247             Value => $o});
248             }
249            
250 0 0       0 if (my $p = $n->prefixes()) {
251 0         0 $self->_pcdata({Name => "vCard:prefix",
252             Value => $p});
253             }
254            
255 0 0       0 if (my $s = $n->suffixes()) {
256 0         0 $self->_pcdata({Name => "vCard:suffix",
257             Value => $s});
258             }
259            
260 0         0 $self->end_element({Name => "vCard:n"});
261             }
262            
263 1         45 return 1;
264             }
265              
266             =head2 $obj->_render_nickname(Text::vCard)
267              
268             =cut
269              
270             sub _render_nickname {
271 1     1   21 my $self = shift;
272 1         3 my $vcard = shift;
273              
274 1 50       5 if (my $nick = $vcard->nickname()) {
275 0         0 $self->_pcdata({Name => "vCard:nickname",
276             Value => $nick});
277             }
278              
279 1         31 return 1;
280             }
281              
282             =head2 $obj->_render_photo(Text::vCard)
283              
284             =cut
285              
286             sub _render_photo {
287 1     1   2 my $self = shift;
288 1         2 my $vcard = shift;
289              
290 1         12 my $photos = $vcard->get({"node_type" => "photo"});
291              
292 1 50       18 if (! $photos) {
293 1         2 return 1;
294             }
295            
296 0         0 foreach my $p (@$photos) {
297 0         0 $self->_media({name => "vCard:photo",
298             media => $p});
299             }
300            
301 0         0 return 1;
302             }
303              
304              
305             =head2 $obj->_render_bday(Text::vCard)
306              
307             =cut
308              
309             sub _render_bday {
310 1     1   3 my $self = shift;
311 1         1 my $vcard = shift;
312              
313 1 50       6 if (my $bday = $vcard->bday()) {
314 0         0 $self->_pcdata({Name => "vCard:bday",
315             Value => $bday});
316             }
317              
318 1         22 return 1;
319             }
320              
321             =head2 $obj->_render_adrs(Text::vCard)
322              
323             =cut
324              
325             sub _render_adrs {
326 1     1   2 my $self = shift;
327 1         2 my $vcard = shift;
328              
329 1         6 my $addresses = $vcard->get({"node_type" => "addresses"});
330              
331 1 50       21 if (! $addresses) {
332 0         0 return 1;
333             }
334              
335             #
336              
337 1         4 foreach my $adr (@$addresses) {
338            
339 1         111 my $types = join(";",$adr->types());
340            
341 1         64 $self->start_element({Name => "vCard:adr",
342             Attributes => {"{}del.type" => {Name => "vCard:del.type",
343             Value => $types}}
344             });
345            
346 1 50       22 if (my $p = $adr->po_box()) {
347 0         0 $self->_pcdata({Name => "vCard:pobox",
348             Value => $p});
349             }
350            
351 1 50       30 if (my $e = $adr->extended()) {
352 0         0 $self->_pcdata({Name => "vCard:extadr",
353             Value => $e});
354             }
355            
356 1 50       145 if (my $s = $adr->street()) {
357 1         1699 $self->_pcdata({Name => "vCard:street",
358             Value => $s});
359             }
360            
361 1 50       17 if (my $c = $adr->city()) {
362 1         20 $self->_pcdata({Name => "vCard:locality",
363             Value => $c});
364             }
365            
366 1 50       8 if (my $r = $adr->region()) {
367 1         19 $self->_pcdata({Name => "vCard:region",
368             Value => $r});
369             }
370            
371 1 50       16 if (my $p = $adr->post_code()) {
372 0         0 $self->_pcdata({Name => "vCard:pcode",
373             Value => $p});
374             }
375            
376 1 50       26 if (my $c = $adr->country()) {
377 1         20 $self->_pcdata({Name => "vCard:country",
378             Value => $c});
379             }
380            
381 1         7 $self->end_element({Name=>"vCard:adr"});
382             }
383            
384 1         12 return 1;
385             }
386              
387              
388             =head2 $obj->_render_labels(Text::vCard)
389              
390             =cut
391              
392             sub _render_labels {
393 1     1   377 my $self = shift;
394 1         3 my $vcard = shift;
395              
396 1         11 my $labels = $vcard->get({"node_type" => "labels"});
397            
398 1 50       27 if (! $labels) {
399 1         3 return 1;
400             }
401              
402             #
403              
404 0         0 foreach my $l (@$labels) {
405            
406 0         0 my $types = join(";",$l->types());
407            
408 0         0 $self->_pcdata({Name => "vCard:label",
409             Value => $l->value(),
410             Attributes => {"{}del.type" => {Name => "vCard:del.type",
411             Value => $types}}
412             });
413             }
414            
415 0         0 return 1;
416             }
417              
418             =head2 $obj->_render_tels(Text::vCard)
419              
420             =cut
421              
422             sub _render_tels {
423 1     1   1 my $self = shift;
424 1         2 my $vcard = shift;
425              
426 1         4 my $numbers = $vcard->get({"node_type" => "phone"});
427              
428 1 50       18 if (! $numbers) {
429 1         3 return 1;
430             }
431              
432             #
433              
434 0         0 foreach my $tel (@$numbers) {
435            
436 0         0 my $types = join(";",$tel->types());
437            
438 0         0 $self->_pcdata({Name => "vCard:tel",
439             Value => $tel->value(),
440             Attributes => {"{}tel.type" => {Name => "vCard:tel.type",
441             Value => $types}}
442             });
443             }
444            
445 0         0 return 1;
446             }
447              
448             =head2 $obj->_render_emails(Text::vCard)
449              
450             =cut
451              
452             sub _render_emails {
453 1     1   1 my $self = shift;
454 1         2 my $vcard = shift;
455              
456 1         5 my $addresses = $vcard->get({"node_type" => "email"});
457              
458 1 50       28 if (! $addresses) {
459 1         2 return 1;
460             }
461              
462             #
463              
464 0         0 foreach my $e (@$addresses) {
465              
466 0         0 my $types = join(";",$e->types());
467            
468 0         0 $self->_pcdata({Name => "vCard:email",
469             Value => $e->value(),
470             Attributes => {"{}email.type" => {Name => "vCard:email.type",
471             Value => $types}}
472             });
473             }
474            
475 0         0 return 1;
476             }
477              
478             =head2 $obj->_render_instantmessaging(Text::vCard)
479              
480             =cut
481              
482             sub _render_instantmessaging {
483 1     1   2 my $self = shift;
484 1         2 my $vcard = shift;
485              
486 1         5 my $im_list = $self->_im_services();
487              
488 1         9 foreach my $service (sort {$a cmp $b} keys %$im_list) {
  7         12  
489              
490 5         20 my $addresses = $vcard->get({"node_type" => "x-$service"});
491            
492 5 50       82 if (! $addresses) {
493 5         10 next;
494             }
495              
496 0         0 foreach my $im (@$addresses) {
497            
498 0         0 my $types = join(";",$im->types());
499            
500 0         0 $self->_pcdata({Name => $im_list->{$service},
501             Value => $im->value(),
502             Attributes => {"{}im.type"=> {Name => "vCard:im.type",
503             Value => $types}}
504             });
505             }
506             }
507              
508 1         5 return 1;
509             }
510              
511             =head2 $obj->_render_mailer(Text::vCard)
512              
513             =cut
514              
515             sub _render_mailer {
516 1     1   2 my $self = shift;
517 1         3 my $vcard = shift;
518              
519 1 50       4 if (my $m = $vcard->mailer()) {
520              
521 0         0 $self->_pcdata({Name => "vCard:mailer",
522             Value => $m});
523             }
524              
525 1         21 return 1;
526             }
527              
528             =head2 $obj->_render_tz(Text::vCard)
529              
530             =cut
531              
532             sub _render_tz {
533 1     1   1 my $self = shift;
534 1         12 my $vcard = shift;
535              
536 1 50       6 if (my $tz = $vcard->tz()) {
537              
538 0         0 $self->_pcdata({Name => "vCard:tz",
539             Value => $tz});
540             }
541              
542 1         21 return 1;
543             }
544              
545             =head2 $obj->_render_geo(Text::vCard)
546              
547             =cut
548              
549             sub _render_geo {
550 1     1   2 my $self = shift;
551 1         2 my $vcard = shift;
552              
553 1         5 my $geo = $vcard->get({"node_type" => "geo"});
554              
555 1 50       18 if (! $geo) {
556 1         3 return 1;
557             }
558              
559 0         0 $geo = $geo->[0];
560              
561             #
562              
563 0         0 $self->start_element({Name => "vCard:geo"});
564              
565 0         0 $self->_pcdata({Name => "vCard:lat",
566             Value => $geo->lat()});
567              
568 0         0 $self->_pcdata({Name => "vCard:lon",
569             Value => $geo->long()});
570              
571 0         0 $self->end_element({Name => "vCard:geo"});
572 0         0 return 1;
573             }
574              
575             =head2 $obj->_render_org(Text::vCard)
576              
577             =cut
578              
579             sub _render_org {
580 1     1   3 my $self = shift;
581 1         8 my $vcard = shift;
582              
583 1         12 my $orgs = $vcard->get({"node_type" => "org"});
584              
585 1 50       20 if (! $orgs) {
586 0         0 return 1;
587             }
588              
589             #
590              
591 1         4 foreach my $o (@$orgs) {
592 1         4 $self->start_element({Name => "vCard:org"});
593              
594 1 50       21 if (my $name = $o->name()) {
595              
596 1         18 $self->_pcdata({Name => "vCard:orgnam",
597             Value => $name});
598             }
599              
600 1 50       8 if (my $units = $o->unit()) {
601              
602 1         13 foreach my $u (grep { /\w/ } @$units) {
  1         49  
603 0         0 $self->_pcdata({Name => "vCard:orgunit",
604             Value => $u});
605             }
606             }
607              
608 1         6 $self->end_element({Name => "vCard:org"});
609             }
610              
611 1         10 return 1;
612             }
613              
614             =head2 $obj->_render_title(Text::vCard)
615              
616             =cut
617              
618             sub _render_title {
619 1     1   2 my $self = shift;
620 1         2 my $vcard = shift;
621              
622 1 50       5 if (my $t = $vcard->title()) {
623              
624 0         0 $self->_pcdata({Name => "vCard:title",
625             Value => $t});
626             }
627              
628 1         18 return 1;
629             }
630              
631             =head2 $obj->_render_role(Text::vCard)
632              
633             =cut
634              
635             sub _render_role {
636 1     1   2 my $self = shift;
637 1         1 my $vcard = shift;
638              
639 1 50       4 if (my $r = $vcard->role()) {
640              
641 0         0 $self->_pcdata({Name => "vCard:role",
642             Value => $r});
643             }
644              
645 1         17 return 1;
646             }
647              
648             =head2 $obj->_render_logo(Text::vCard)
649              
650             =cut
651              
652             sub _render_logo {
653 1     1   2 my $self = shift;
654 1         1 my $vcard = shift;
655              
656 1         6 my $logos = $vcard->get({"node_type" => "logo"});
657              
658 1 50       14 if (! $logos) {
659 1         2 return 1;
660             }
661              
662 0         0 foreach my $l (@$logos) {
663              
664 0         0 $self->_media({name => "vCard:logo",
665             media => $l});
666             }
667              
668 0         0 return 1;
669             }
670              
671             =head2 $obj->_render_categories(Text::vCard)
672              
673             =cut
674              
675             sub _render_categories {
676 1     1   2 my $self = shift;
677 1         2 my $vcard = shift;
678              
679 1   33     4 my $cats = $vcard->get({"node_type" => 'categories'}) ||
680             $vcard->get({"node_type" => 'category'});
681              
682 1 50       32 if (! $cats) {
683 0         0 return 1;
684             }
685              
686             #
687              
688 1         5 $self->start_element({Name => "vCard:categories"});
689            
690 1         14 foreach (split(",",$cats->[0]->value())) {
691            
692 1         19 $self->_pcdata({Name => "vCard:item",
693             Value => $_});
694             }
695            
696 1         7 $self->end_element({Name => "vCard:categories"});
697 1         7 return 1;
698             }
699              
700             =head2 $obj->_render_note(Text::vCard)
701              
702             =cut
703              
704             sub _render_note {
705 1     1   8 my $self = shift;
706 1         1 my $vcard = shift;
707              
708 1         5 my $n = $vcard->get({"node_type" => "note"});
709              
710 1 50       17 if (! $n) {
711 0         0 return 1;
712             }
713              
714 1 50       5 if (my $n = $vcard->note()) {
715 1         60 $self->_pcdata({Name => "vCard:note",
716             CDATA => 1,
717             Value => $n});
718             }
719            
720 1         3 return 1;
721             }
722              
723             =head2 $self->_render_sound(Text::vCard)
724              
725             =cut
726              
727             sub _render_sound {
728 1     1   2 my $self = shift;
729 1         2 my $vcard = shift;
730              
731 1         5 my $snds = $vcard->get({"node_type" => "sound"});
732              
733 1 50       15 if (! $snds) {
734 1         2 return 1;
735             }
736              
737 0         0 foreach my $s (@$snds) {
738 0         0 $self->_media({name => "vCard:sound",
739             media => $s});
740             }
741              
742 0         0 return 1;
743             }
744              
745             =head2 $self->_render_url(Text::vCard)
746              
747             =cut
748              
749             sub _render_url {
750 1     1   2 my $self = shift;
751 1         2 my $vcard = shift;
752              
753 1 50       4 if (my $url = $vcard->url()) {
754 0         0 $self->_pcdata({Name => "vCard:url",
755             Attributes => {"{}uri" => {Name => "vCard:uri",
756             Value => $url}}});
757             }
758            
759 1         27 return 1;
760             }
761              
762             =head2 $obj->_render_key(Text::vCard)
763              
764             =cut
765              
766             sub _render_key {
767 1     1   2 my $self = shift;
768 1         1 my $vcard = shift;
769              
770 1         4 my $keys = $vcard->get({"node_type" => "key"});
771              
772 1 50       14 if (! $keys) {
773 1         2 return 1;
774             }
775              
776 0         0 foreach my $k (@$keys) {
777 0         0 $self->_media({name => "vCard:key",
778             media => $k});
779             }
780              
781 0         0 return 1;
782             }
783              
784             =head2 $obj->_render_custom(Text::vCard)
785              
786             By default this method does nothing. It is here to
787             be subclassed.
788              
789             =cut
790              
791 1     1   1 sub _render_custom { }
792              
793             =head2 $obj->_im_services()
794              
795             Returns a hash ref mapping an instant messaging service
796             type to an XML element. Default is :
797              
798             {"aim" => "foaf:aimChatID",
799             "yahoo" => "foaf:yahooChatID",
800             "msn" => "foaf:msnChatID",
801             "jabber" => "foaf:JabberID",
802             "icq" => "foaf:icqChatId"}
803              
804             This is called by the I<_render_instantmessaging> method.
805              
806             =cut
807              
808             sub _im_services {
809 1     1   8 return {"aim" => "foaf:aimChatID",
810             "yahoo" => "foaf:yahooChatID",
811             "msn" => "foaf:msnChatID",
812             "jabber" => "foaf:JabberID",
813             "icq" => "foaf:icqChatID"};
814             }
815              
816             sub _pcdata {
817 8     8   60 my $self = shift;
818 8         11 my $data = shift;
819              
820 8         21 $self->start_element($data);
821              
822 8 100       99 if ($data->{CDATA}) {
823 1         19 $self->start_cdata();
824             }
825              
826 8 50       49 if ($data->{Value}) {
827 8         30 $self->characters({Data => encode_utf8($data->{Value})});
828             }
829              
830 8 100       958 if ($data->{CDATA}) {
831 1         9 $self->end_cdata();
832             }
833              
834 8         50 $self->end_element($data);
835 8         777 return 1;
836             }
837              
838             sub _media {
839 0     0   0 my $self = shift;
840 0         0 my $data = shift;
841              
842 0         0 my $attrs = {};
843              
844             # as in not 'key' and not something pointing to an 'uri'
845              
846 0 0 0     0 if (($data->{name} !~ /^k/) && ($data->{type})) {
847              
848             # as in 'photo' or 'logo'
849             # and not 'sound'
850            
851 0 0       0 my $mime = ($data->{name} =~ /^[pl]/i) ? "img" : "aud";
852            
853 0         0 $attrs = {"{}$mime.type"=>{Name => "vCard:$mime.type",
854             Value => $data->{type}}};
855             }
856              
857             #
858              
859 0         0 my $obj = $data->{media};
860              
861 0         0 $self->start_element({Name => $data->{name},
862             Attributes => $attrs});
863              
864 0 0       0 if ($obj->is_type("base64")) {
865 0         0 $self->_pcdata({Name => "vCard:b64bin",
866             Value => encode_base64($obj->value()),
867             CDATA => 1});
868             }
869              
870             else {
871 0         0 $self->_pcdata({Name => "extref",
872             Attributes => {"{}uri" => {Name => "vCard:uri",
873             Value => $obj->value()}}
874             });
875             }
876              
877 0         0 $self->end_element({Name => $data->{name}});
878 0         0 return 1;
879             }
880              
881             sub start_document {
882 1     1 1 4 my $self = shift;
883              
884 1         34 $self->SUPER::start_document();
885              
886 1         55 $self->xml_decl({Version => "1.0",
887             Encoding => "UTF-8"});
888              
889 1         30 my $ns = $self->namespaces();
890              
891 1         8 foreach my $prefix (keys %$ns) {
892 5         93 $self->start_prefix_mapping({Prefix => $prefix,
893             NamespaceURI => $ns->{$prefix}});
894             }
895            
896 1         12 return 1;
897             }
898              
899             sub end_document {
900 1     1 1 1 my $self = shift;
901              
902 1         2 foreach my $prefix (keys %{$self->namespaces()}) {
  1         4  
903 5         66 $self->end_prefix_mapping({Prefix => $prefix});
904             }
905              
906 1         16 $self->SUPER::end_document();
907 1         27 return 1;
908             }
909              
910             sub start_element {
911 12     12 1 16 my $self = shift;
912 12         14 my $data = shift;
913              
914 12         66 my $name = $self->prepare_qname($data->{Name});
915 12         1513 my $attrs = $self->prepare_attrs($data->{Attributes});
916              
917 12         8199 $self->SUPER::start_element({ %$name, %$attrs });
918             }
919              
920             sub end_element {
921 12     12 1 18 my $self = shift;
922 12         14 my $data = shift;
923              
924 12         33 my $name = $self->prepare_qname($data->{Name});
925              
926 12         1264 $self->SUPER::end_element($name);
927             }
928              
929 0     0     sub DESTROY {}
930              
931             =head1 NAMESPACES
932              
933             This package generates SAX events using the following XML
934             namespaces :
935              
936             =over 4
937              
938             =item * B
939              
940             x-urn:cpan:ascope:xml-generator-vcard#
941              
942             =item * B
943              
944             http://xmlns.com/foaf/0.1/
945              
946             =back
947              
948             =head1 HOW TO
949              
950             =head2 Filter cards by category
951              
952             package MyGenerator;
953             use base qw (XML::Generator::vCard);
954              
955             sub _render_card {
956             my $self = shift;
957             my $card = shift;
958              
959             my $cats = $vcard->get({"node_type" => 'categories'}) ||
960             $vcard->get({"node_type" => 'category'});
961              
962             if (! $cats) {
963             return 1;
964             }
965            
966             if (! grep { $_->value() eq "foo" } split(",",$cats->[0])) {
967             return 1;
968             }
969              
970             return $self->SUPER::_render_card($vcard);
971             }
972              
973             package main;
974              
975             my $writer = XML::SAX::Writer->new();
976             my $parser = MyGenerator->new(Handler=>$writer);
977              
978             $parser->parse_files(@ARGV);
979              
980             =head2 Generate SAX events for a custom 'X-*' field
981              
982             package MyGenerator;
983             use base qw (XML::Generator::vCard);
984              
985             sub _render_custom {
986             my $self = shift;
987             my $vcard = shift;
988              
989             my $custom = $vcard->get({"node_type" => "x-foobar"});
990            
991             if (! $addresses) {
992             next;
993             }
994              
995             foreach my $foo (@$custom) {
996            
997             my $types = join(";",$foo->types());
998              
999             $self->_pcdata({Name => "foo:bar",
1000             Value => $foo->value(),
1001             Attributes => {"{}type"=> {Name => "type",
1002             Value => $types}}
1003             });
1004             }
1005              
1006             return 1;
1007             }
1008            
1009             package main;
1010              
1011             my $writer = XML::SAX::Writer->new();
1012             my $parser = MyGenerator->new(Handler=>$writer);
1013              
1014             $parser->parse_files(@ARGV);
1015              
1016             =head2 Add custom namespaces
1017              
1018             package MyGenerator;
1019             use base qw (XML::Generator::vCard);
1020              
1021             sub namespaces {
1022             my $self = shift;
1023            
1024             my $ns = $self->SUPER::namespaces();
1025             $ns->{ "foo" } = "x-urn:foo:bar#";
1026              
1027             return $ns;
1028             }
1029              
1030             package main;
1031              
1032             my $writer = XML::SAX::Writer->new();
1033             my $parser = MyGenerator->new(Handler=>$writer);
1034              
1035             $parser->parse_files(@ARGV);
1036              
1037             =head1 VERSION
1038              
1039             1.3
1040              
1041             =head1 DATE
1042              
1043             $Date: 2004/12/28 23:31:29 $
1044              
1045             =head1 AUTHOR
1046              
1047             Aaron Straup Cope Eascope@cpan.orgE
1048              
1049             =head1 SEE ALSO
1050              
1051             L
1052              
1053             L
1054              
1055             http://www.ietf.org/rfc/rfc2426.txt
1056              
1057             http://www.ietf.org/rfc/rfc2425.txt
1058              
1059             =head1 BUGS
1060              
1061             vCards containg binary PHOTO images may cause Perl to segfault on
1062             Mac OSX and come flavours of Linux (but not FreeBSD.) The source of
1063             this problem has been traced, I think, to a regular expression issue
1064             in the Perl Text::ParseWords library. A bug report has been filed.
1065              
1066             Please report all other bugs via http://rt.cpan.org
1067              
1068             =head1 LICENSE
1069              
1070             Copyright (c) 2004, Aaron Straup Cope. All Rights Reserved.
1071              
1072             This is free software, you may use it and distribute it
1073             under the same terms as Perl itself.
1074              
1075             =cut
1076              
1077             return 1