| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Text::vCard; | 
| 2 |  |  |  |  |  |  | $Text::vCard::VERSION = '3.08'; | 
| 3 | 15 |  |  | 15 |  | 57479 | use 5.006; | 
|  | 15 |  |  |  |  | 38 |  | 
| 4 | 15 |  |  | 15 |  | 51 | use Carp; | 
|  | 15 |  |  |  |  | 14 |  | 
|  | 15 |  |  |  |  | 675 |  | 
| 5 | 15 |  |  | 15 |  | 67 | use strict; | 
|  | 15 |  |  |  |  | 25 |  | 
|  | 15 |  |  |  |  | 251 |  | 
| 6 | 15 |  |  | 15 |  | 46 | use warnings; | 
|  | 15 |  |  |  |  | 16 |  | 
|  | 15 |  |  |  |  | 458 |  | 
| 7 | 15 |  |  | 15 |  | 1592 | use Text::vFile::asData 0.07; | 
|  | 15 |  |  |  |  | 16064 |  | 
|  | 15 |  |  |  |  | 69 |  | 
| 8 | 15 |  |  | 15 |  | 5619 | use Text::vCard::Node; | 
|  | 15 |  |  |  |  | 26 |  | 
|  | 15 |  |  |  |  | 455 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | # See this module for your basic parser functions | 
| 11 | 15 |  |  | 15 |  | 73 | use base qw(Text::vFile::asData); | 
|  | 15 |  |  |  |  | 14 |  | 
|  | 15 |  |  |  |  | 1209 |  | 
| 12 | 15 |  |  | 15 |  | 59 | use vars qw (%lookup %node_aliases @simple); | 
|  | 15 |  |  |  |  | 16 |  | 
|  | 15 |  |  |  |  | 1700 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | # If the node's data does not break down use this | 
| 15 |  |  |  |  |  |  | my @default_field = qw(value); | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | # If it does use these | 
| 18 |  |  |  |  |  |  | %lookup = ( | 
| 19 |  |  |  |  |  |  | 'ADR' => [ | 
| 20 |  |  |  |  |  |  | 'po_box', 'extended',  'street', 'city', | 
| 21 |  |  |  |  |  |  | 'region', 'post_code', 'country' | 
| 22 |  |  |  |  |  |  | ], | 
| 23 |  |  |  |  |  |  | 'N'   => [ 'family', 'given', 'middle', 'prefixes', 'suffixes' ], | 
| 24 |  |  |  |  |  |  | 'GEO' => [ 'lat',    'long' ], | 
| 25 |  |  |  |  |  |  | 'ORG' => [ 'name',   'unit' ], | 
| 26 |  |  |  |  |  |  | ); | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | %node_aliases = ( | 
| 29 |  |  |  |  |  |  | 'FULLNAME'  => 'FN', | 
| 30 |  |  |  |  |  |  | 'BIRTHDAY'  => 'BDAY', | 
| 31 |  |  |  |  |  |  | 'TIMEZONE'  => 'TZ', | 
| 32 |  |  |  |  |  |  | 'PHONES'    => 'TEL', | 
| 33 |  |  |  |  |  |  | 'ADDRESSES' => 'ADR', | 
| 34 |  |  |  |  |  |  | 'NAME'      => 'N',      # To be deprecated as clashes with RFC | 
| 35 |  |  |  |  |  |  | 'MONIKER'   => 'N', | 
| 36 |  |  |  |  |  |  | ); | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | # Generate all our simple methods | 
| 39 |  |  |  |  |  |  | @simple | 
| 40 |  |  |  |  |  |  | = qw(FN BDAY MAILER TZ TITLE ROLE NOTE PRODID REV SORT-STRING UID URL CLASS FULLNAME BIRTHDAY TIMEZONE NAME EMAIL NICKNAME PHOTO); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | # Now we want lowercase as well | 
| 43 |  |  |  |  |  |  | map { push( @simple, lc($_) ) } @simple; | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | # Generate the methods | 
| 46 |  |  |  |  |  |  | { | 
| 47 | 15 |  |  | 15 |  | 64 | no strict 'refs'; | 
|  | 15 |  |  |  |  | 16 |  | 
|  | 15 |  |  |  |  | 380 |  | 
| 48 | 15 |  |  | 15 |  | 48 | no warnings 'redefine'; | 
|  | 15 |  |  |  |  | 19 |  | 
|  | 15 |  |  |  |  | 16914 |  | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | # 'version' handled separately | 
| 51 |  |  |  |  |  |  | # to prevent conflict with ExtUtils::MakeMaker | 
| 52 |  |  |  |  |  |  | # and $VERSION | 
| 53 |  |  |  |  |  |  | for my $node ( @simple, "version" ) { | 
| 54 |  |  |  |  |  |  | *$node = sub { | 
| 55 | 117 |  |  | 117 |  | 484 | my ( $self, $value ) = @_; | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | # See if we have it already | 
| 58 | 117 |  |  |  |  | 140 | my $nodes = $self->get($node); | 
| 59 | 117 | 100 | 100 |  |  | 243 | if ( !defined $nodes && $value ) { | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | # Add it as a node if not exists and there is a value | 
| 62 | 33 |  |  |  |  | 87 | $self->add_node( { 'node_type' => $node, } ); | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | # Get it out again | 
| 65 | 33 |  |  |  |  | 53 | $nodes = $self->get($node); | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 | 117 | 100 | 100 |  |  | 348 | if ( scalar($nodes) && $value ) { | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # Set it | 
| 71 | 34 |  |  |  |  | 155 | $nodes->[0]->value($value); | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 117 | 100 |  |  |  | 401 | return $nodes->[0]->value() if scalar($nodes); | 
| 75 | 1 |  |  |  |  | 4 | return undef; | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | =head1 NAME | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | Text::vCard - Edit and create vCards (RFC 2426) | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | =head1 WARNING | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | L and L are built on top of this module and provide | 
| 87 |  |  |  |  |  |  | a more intuitive user interface.  Please try those modules first. | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | use Text::vCard; | 
| 92 |  |  |  |  |  |  | my $cards | 
| 93 |  |  |  |  |  |  | = Text::vCard->new( { 'asData_node' => $objects_node_from_asData, } ); | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | A vCard is an electronic business card. | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | This package is for a single vCard (person / record / set of address | 
| 100 |  |  |  |  |  |  | information). It provides an API to editing and creating vCards, or supplied | 
| 101 |  |  |  |  |  |  | a specific piece of the Text::vFile::asData results it generates a vCard | 
| 102 |  |  |  |  |  |  | with that content. | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | You should really use L as this handles creating | 
| 105 |  |  |  |  |  |  | vCards from an existing file for you. | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | =head1 METHODS | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | =head2 new() | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | use Text::vCard; | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | my $new_vcard = Text::vCard->new(); | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | my $existing_vcard | 
| 116 |  |  |  |  |  |  | = Text::vCard->new( { 'asData_node' => $objects_node_from_asData, } ); | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | =cut | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | sub new { | 
| 121 | 39 |  |  | 39 | 1 | 960 | my ( $proto, $conf ) = @_; | 
| 122 | 39 |  | 100 |  |  | 182 | my $class = ref($proto) || $proto; | 
| 123 | 39 |  |  |  |  | 50 | my $self = {}; | 
| 124 |  |  |  |  |  |  |  | 
| 125 | 39 |  |  |  |  | 75 | bless( $self, $class ); | 
| 126 |  |  |  |  |  |  |  | 
| 127 | 38 |  | 100 |  |  | 165 | $self->{encoding_out} = $conf->{encoding_out} || 'UTF-8'; | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 38 |  |  |  |  | 39 | my %nodes; | 
| 130 | 38 |  |  |  |  | 53 | $self->{nodes} = \%nodes; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 38 | 100 |  |  |  | 86 | if ( defined $conf->{'asData_node'} ) { | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | # Have a vcard data node being passed in | 
| 135 | 26 |  |  |  |  | 34 | while ( my ( $node_type, $data ) = each %{ $conf->{'asData_node'} } ) | 
|  | 228 |  |  |  |  | 557 |  | 
| 136 |  |  |  |  |  |  | { | 
| 137 | 202 |  |  |  |  | 140 | my $group; | 
| 138 | 202 | 100 |  |  |  | 354 | if ( $node_type =~ /\./ ) { | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | # Version 3.0 supports group types, we do not | 
| 141 |  |  |  |  |  |  | # so remove everything before '.' | 
| 142 | 14 |  |  |  |  | 61 | ( $group, $node_type ) = $node_type =~ /(.+)\.(.*)/; | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | # Deal with each type (ADR, FN, TEL etc) | 
| 146 |  |  |  |  |  |  | $self->_add_node( | 
| 147 | 202 |  |  |  |  | 479 | {   'node_type' => $node_type, | 
| 148 |  |  |  |  |  |  | 'data'      => $data, | 
| 149 |  |  |  |  |  |  | 'group'     => $group, | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  | ); | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  | }    # else we're creating a new vCard | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 38 |  |  |  |  | 78 | return $self; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | =head2 add_node() | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | my $address = $vcard->add_node( { 'node_type' => 'ADR', } ); | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | This creates a new address (a L object) in the vCard | 
| 163 |  |  |  |  |  |  | which you can then call the address methods on. See below for what options are available. | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | The node_type parameter must conform to the vCard spec format (e.g. ADR not address) | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | =cut | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | sub add_node { | 
| 170 | 67 |  |  | 67 | 1 | 1101 | my ( $self, $conf ) = @_; | 
| 171 |  |  |  |  |  |  | croak 'Must supply a node_type' | 
| 172 | 67 | 100 | 100 |  |  | 484 | unless defined $conf && defined $conf->{'node_type'}; | 
| 173 | 65 | 100 |  |  |  | 99 | unless ( defined $conf->{data} ) { | 
| 174 | 36 |  |  |  |  | 25 | my %empty; | 
| 175 | 36 |  |  |  |  | 44 | my @data = ( \%empty ); | 
| 176 | 36 |  |  |  |  | 50 | $conf->{'data'} = \@data; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 65 |  |  |  |  | 85 | $self->_add_node($conf); | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | =head2 get() | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | The following method allows you to extract the contents from the vCard. | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | # get all elements | 
| 187 |  |  |  |  |  |  | $nodes = $vcard->get('tel'); | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | # Just get the home address | 
| 190 |  |  |  |  |  |  | my $nodes = $vcard->get( | 
| 191 |  |  |  |  |  |  | {   'node_type' => 'addresses', | 
| 192 |  |  |  |  |  |  | 'types'     => 'home', | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  | ); | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | # get all phone number that matches serveral types | 
| 197 |  |  |  |  |  |  | my @types = qw(work home); | 
| 198 |  |  |  |  |  |  | my $nodes = $vcard->get( | 
| 199 |  |  |  |  |  |  | {   'node_type' => 'tel', | 
| 200 |  |  |  |  |  |  | 'types'     => \@types, | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  | ); | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | Either an array or array ref is returned, containing | 
| 206 |  |  |  |  |  |  | L objects.  If there are no results of 'node_type' | 
| 207 |  |  |  |  |  |  | undef is returned. | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | Supplied with a scalar or an array ref the methods | 
| 210 |  |  |  |  |  |  | return a list of nodes of a type, where relevant. If any | 
| 211 |  |  |  |  |  |  | of the elements is the prefered element it will be | 
| 212 |  |  |  |  |  |  | returned as the first element of the list. | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | =cut | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | sub get { | 
| 217 | 361 |  |  | 361 | 1 | 3712 | my ( $self, $conf ) = @_; | 
| 218 | 361 | 100 |  |  |  | 611 | carp "You did not supply an element type" unless defined $conf; | 
| 219 | 360 | 100 |  |  |  | 482 | if ( ref($conf) eq 'HASH' ) { | 
| 220 |  |  |  |  |  |  | return $self->get_of_type( $conf->{'node_type'}, $conf->{'types'} ) | 
| 221 | 12 | 100 |  |  |  | 42 | if defined $conf->{'types'}; | 
| 222 | 6 |  |  |  |  | 22 | return $self->get_of_type( $conf->{'node_type'} ); | 
| 223 |  |  |  |  |  |  | } else { | 
| 224 | 348 |  |  |  |  | 430 | return $self->get_of_type($conf); | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | =head2 get_simple_type() | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | The following method is a convenience wrapper for accessing simple elements. | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | $value = $vcard->get_simple_type( 'email', [ 'internet', 'work' ] ); | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | If multiple elements match, then only the first is returned.  If the object | 
| 235 |  |  |  |  |  |  | isn't found, or doesn't have a simple value, then undef is returned. | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | The argument type may be ommitted, it can be a scalar, or it can be an | 
| 238 |  |  |  |  |  |  | array reference if multiple types are selected. | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | =cut | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | sub get_simple_type { | 
| 243 | 1 |  |  | 1 | 1 | 226 | my ( $self, $node_type, $types ) = @_; | 
| 244 | 1 | 50 |  |  |  | 3 | carp "You did not supply an element type" unless defined $node_type; | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 1 |  |  |  |  | 2 | my %hash = ( 'node_type', $node_type ); | 
| 247 | 1 | 50 |  |  |  | 4 | $hash{'types'} = $types if defined $types; | 
| 248 | 1 |  |  |  |  | 6 | my $node = $self->get( \%hash ); | 
| 249 | 1 | 50 | 33 |  |  | 4 | return undef unless $node && @{$node} > 0 && exists $node->[0]->{'value'}; | 
|  | 1 |  | 33 |  |  | 9 |  | 
| 250 |  |  |  |  |  |  |  | 
| 251 | 1 |  |  |  |  | 3 | $node->[0]->{'value'}; | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | =head2 nodes | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | my $addresses = $vcard->get( { 'node_type' => 'address' } ); | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | my $first_address = $addresses->[0]; | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | # get the value | 
| 261 |  |  |  |  |  |  | print $first_address->street(); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | # set the value | 
| 264 |  |  |  |  |  |  | $first_address->street('Barney Rubble'); | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | # See if it is part of a group | 
| 267 |  |  |  |  |  |  | if ( $first_address->group() ) { | 
| 268 |  |  |  |  |  |  | print 'Group: ' . $first_address->group(); | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | According to the RFC the following 'simple' nodes should only have one | 
| 272 |  |  |  |  |  |  | element, this is not enforced by this module, so for example you can | 
| 273 |  |  |  |  |  |  | have multiple URL's if you wish. | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | =head2 simple nodes | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | For simple nodes, you can also access the first node in the following way: | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | my $fn = $vcard->fullname(); | 
| 280 |  |  |  |  |  |  | # or setting | 
| 281 |  |  |  |  |  |  | $vcard->fullname('new name'); | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | The node will be automatically created if it does not exist and you | 
| 284 |  |  |  |  |  |  | supplied a value.  undef is returned if the node does not | 
| 285 |  |  |  |  |  |  | exist. Simple nodes can be called as all upper or all lowercase method | 
| 286 |  |  |  |  |  |  | names. | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | vCard Spec: 'simple'    Alias | 
| 289 |  |  |  |  |  |  | --------------------    -------- | 
| 290 |  |  |  |  |  |  | FN                      fullname | 
| 291 |  |  |  |  |  |  | BDAY                    birthday | 
| 292 |  |  |  |  |  |  | MAILER | 
| 293 |  |  |  |  |  |  | TZ                      timezone | 
| 294 |  |  |  |  |  |  | TITLE | 
| 295 |  |  |  |  |  |  | ROLE | 
| 296 |  |  |  |  |  |  | NOTE | 
| 297 |  |  |  |  |  |  | PRODID | 
| 298 |  |  |  |  |  |  | REV | 
| 299 |  |  |  |  |  |  | SORT-STRING | 
| 300 |  |  |  |  |  |  | UID | 
| 301 |  |  |  |  |  |  | URL | 
| 302 |  |  |  |  |  |  | CLASS | 
| 303 |  |  |  |  |  |  | EMAIL | 
| 304 |  |  |  |  |  |  | NICKNAME | 
| 305 |  |  |  |  |  |  | PHOTO | 
| 306 |  |  |  |  |  |  | version (lowercase only) | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | =head2 more complex vCard nodes | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | vCard Spec    Alias           Methods on object | 
| 311 |  |  |  |  |  |  | ----------    ----------      ----------------- | 
| 312 |  |  |  |  |  |  | N             name (depreciated as conflicts with rfc, use moniker) | 
| 313 |  |  |  |  |  |  | N             moniker            'family','given','middle','prefixes','suffixes' | 
| 314 |  |  |  |  |  |  | ADR           addresses       'po_box','extended','street','city','region','post_code','country' | 
| 315 |  |  |  |  |  |  | GEO                           'lat','long' | 
| 316 |  |  |  |  |  |  | TEL           phones | 
| 317 |  |  |  |  |  |  | LABELS | 
| 318 |  |  |  |  |  |  | ORG                           'name','unit' (unit is a special case and will return an array reference) | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | my $addresses = $vcard->get( { 'node_type' => 'addresses' } ); | 
| 321 |  |  |  |  |  |  | foreach my $address ( @{$addresses} ) { | 
| 322 |  |  |  |  |  |  | print $address->street(); | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | # Setting values on an address element | 
| 326 |  |  |  |  |  |  | $addresses->[0]->street('The burrows'); | 
| 327 |  |  |  |  |  |  | $addresses->[0]->region('Wimbeldon common'); | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | # Checking an address is a specific type | 
| 330 |  |  |  |  |  |  | $addresses->[0]->is_type('fax'); | 
| 331 |  |  |  |  |  |  | $addresses->[0]->add_types('home'); | 
| 332 |  |  |  |  |  |  | $addresses->[0]->remove_types('work'); | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | =head2 get_group() | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | my $group_name = 'item1'; | 
| 337 |  |  |  |  |  |  | my $node_type  = 'X-ABLABEL'; | 
| 338 |  |  |  |  |  |  | my $of_group   = $vcard->get_group( $group_name, $node_type ); | 
| 339 |  |  |  |  |  |  | foreach my $label ( @{$of_group} ) { | 
| 340 |  |  |  |  |  |  | print $label->value(); | 
| 341 |  |  |  |  |  |  | } | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | This method takes one or two arguments. The group name | 
| 344 |  |  |  |  |  |  | (accessable on any node object by using $node->group() - not | 
| 345 |  |  |  |  |  |  | all nodes will have a group, indeed most vcards do not seem | 
| 346 |  |  |  |  |  |  | to use it) and optionally the types of node you with to | 
| 347 |  |  |  |  |  |  | have returned. | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | Either an array or array reference is returned depending | 
| 350 |  |  |  |  |  |  | on the calling context, if there are no matches it will | 
| 351 |  |  |  |  |  |  | be empty. | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | =cut | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | sub get_group { | 
| 356 | 4 |  |  | 4 | 1 | 687 | my ( $self, $group_name, $node_type ) = @_; | 
| 357 | 4 |  |  |  |  | 4 | my @to_return; | 
| 358 |  |  |  |  |  |  |  | 
| 359 | 4 | 100 | 66 |  |  | 130 | carp "No group name supplied" | 
| 360 |  |  |  |  |  |  | unless defined $group_name | 
| 361 |  |  |  |  |  |  | and $group_name ne ''; | 
| 362 |  |  |  |  |  |  |  | 
| 363 | 3 |  |  |  |  | 3 | $group_name = lc($group_name); | 
| 364 |  |  |  |  |  |  |  | 
| 365 | 3 | 100 | 66 |  |  | 9 | if ( defined $node_type && $node_type ne '' ) { | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | # After a specific node type | 
| 368 | 1 |  |  |  |  | 3 | my $nodes = $self->get($node_type); | 
| 369 | 1 |  |  |  |  | 2 | foreach my $node ( @{$nodes} ) { | 
|  | 1 |  |  |  |  | 2 |  | 
| 370 | 2 | 100 |  |  |  | 4 | push( @to_return, $node ) if $node->group() eq $group_name; | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  | } else { | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | # We want everything from that group | 
| 375 | 2 |  |  |  |  | 1 | foreach my $node_loop ( keys %{ $self->{nodes} } ) { | 
|  | 2 |  |  |  |  | 6 |  | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | # Loop through each type | 
| 378 | 12 |  |  |  |  | 13 | my $nodes = $self->get($node_loop); | 
| 379 | 12 |  |  |  |  | 10 | foreach my $node ( @{$nodes} ) { | 
|  | 12 |  |  |  |  | 12 |  | 
| 380 | 18 | 100 |  |  |  | 20 | if ( $node->group() ) { | 
| 381 | 8 | 100 |  |  |  | 9 | push( @to_return, $node ) | 
| 382 |  |  |  |  |  |  | if $node->group() eq $group_name; | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  | } | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  | } | 
| 387 | 3 | 100 |  |  |  | 10 | return wantarray ? @to_return : \@to_return; | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | =head1 BINARY METHODS | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | These methods allow access to what are potentially binary values such | 
| 393 |  |  |  |  |  |  | as a photo or sound file. Binary values will be correctly encoded and | 
| 394 |  |  |  |  |  |  | decoded to/from base 64. | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | API still to be finalised. | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | =head2 photo() | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | =head2 sound() | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | =head2 key() | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | =head2 logo() | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | =cut | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  | 0 |  |  | sub DESTROY { | 
| 409 |  |  |  |  |  |  | } | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | =head2 get_lookup | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | This method is used internally to lookup those nodes which have | 
| 414 |  |  |  |  |  |  | multiple elements, e.g. GEO has lat and long, N (name) has family, | 
| 415 |  |  |  |  |  |  | given, middle etc. | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | If you wish to extend this package (for custom attributes), overload | 
| 418 |  |  |  |  |  |  | this method in your code: | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | sub my_lookup { | 
| 421 |  |  |  |  |  |  | return \%my_lookup; | 
| 422 |  |  |  |  |  |  | } | 
| 423 |  |  |  |  |  |  | *Text::vCard::get_lookup = \&my_lookup; | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | This has not been tested yet. | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | =cut | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | sub get_lookup { | 
| 430 | 267 |  |  | 267 | 1 | 203 | my $self = shift; | 
| 431 | 267 |  |  |  |  | 273 | return \%lookup; | 
| 432 |  |  |  |  |  |  | } | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | =head2 get_of_type() | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | my $list = $vcard->get_of_type( $node_type, \@types ); | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | It is probably easier just to use the get() method, which inturn calls | 
| 439 |  |  |  |  |  |  | this method. | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =cut | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | # Used to get the right elements | 
| 444 |  |  |  |  |  |  | sub get_of_type { | 
| 445 | 366 |  |  | 366 | 1 | 1243 | my ( $self, $node_type, $types ) = @_; | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | # Upper case the name | 
| 448 | 366 |  |  |  |  | 345 | $node_type = uc($node_type); | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | # See if there is an alias for it | 
| 451 |  |  |  |  |  |  | $node_type = uc( $node_aliases{$node_type} ) | 
| 452 | 366 | 100 |  |  |  | 581 | if defined $node_aliases{$node_type}; | 
| 453 |  |  |  |  |  |  |  | 
| 454 | 366 | 100 |  |  |  | 579 | return undef unless defined $self->{nodes}->{$node_type}; | 
| 455 |  |  |  |  |  |  |  | 
| 456 | 317 | 100 |  |  |  | 323 | if ($types) { | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | # After specific types | 
| 459 | 10 |  |  |  |  | 8 | my @of_type; | 
| 460 | 10 | 100 |  |  |  | 19 | if ( ref($types) eq 'ARRAY' ) { | 
| 461 | 2 |  |  |  |  | 1 | @of_type = @{$types}; | 
|  | 2 |  |  |  |  | 5 |  | 
| 462 |  |  |  |  |  |  | } else { | 
| 463 | 8 |  |  |  |  | 12 | push( @of_type, $types ); | 
| 464 |  |  |  |  |  |  | } | 
| 465 | 10 |  |  |  |  | 7 | my @to_return; | 
| 466 | 10 |  |  |  |  | 9 | foreach my $element ( @{ $self->{nodes}->{$node_type} } ) { | 
|  | 10 |  |  |  |  | 21 |  | 
| 467 | 23 |  |  |  |  | 17 | my $check = 1;    # assum ok for now | 
| 468 | 23 |  |  |  |  | 20 | foreach my $type (@of_type) { | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | # set it as bad if we don't match | 
| 471 | 29 | 100 |  |  |  | 46 | $check = 0 unless $element->is_type($type); | 
| 472 |  |  |  |  |  |  | } | 
| 473 | 23 | 100 |  |  |  | 43 | if ( $check == 1 ) { | 
| 474 |  |  |  |  |  |  |  | 
| 475 | 14 |  |  |  |  | 17 | push( @to_return, $element ); | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  | } | 
| 478 |  |  |  |  |  |  |  | 
| 479 | 10 | 100 |  |  |  | 21 | return undef unless scalar(@to_return); | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | # Make prefered value first | 
| 482 | 9 |  |  |  |  | 18 | @to_return = sort { _sort_prefs($b) <=> _sort_prefs($a) } @to_return; | 
|  | 6 |  |  |  |  | 10 |  | 
| 483 |  |  |  |  |  |  |  | 
| 484 | 9 | 100 |  |  |  | 27 | return wantarray ? @to_return : \@to_return; | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | } else { | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | # Return them all | 
| 489 |  |  |  |  |  |  | return wantarray | 
| 490 | 8 |  |  |  |  | 23 | ? @{ $self->{nodes}->{$node_type} } | 
| 491 | 307 | 100 |  |  |  | 615 | : $self->{nodes}->{$node_type}; | 
| 492 |  |  |  |  |  |  | } | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | =head2 as_string | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | Returns the vCard as a string. | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | =cut | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | sub as_string { | 
| 502 | 22 |  |  | 22 | 1 | 4220 | my ( $self, $fields ) = @_; | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | # derp | 
| 505 | 22 | 50 |  |  |  | 23 | my %e = map { lc $_ => 1 } @{ $fields || [] }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 22 |  |  |  |  | 99 |  | 
| 506 |  |  |  |  |  |  |  | 
| 507 | 22 |  |  |  |  | 46 | my @k = qw(VERSION N FN); | 
| 508 | 22 | 50 |  |  |  | 39 | if ($fields) { | 
| 509 | 0 |  |  |  |  | 0 | push @k, sort map { uc $_ } @$fields; | 
|  | 0 |  |  |  |  | 0 |  | 
| 510 |  |  |  |  |  |  | } else { | 
| 511 | 141 |  |  |  |  | 292 | push @k, grep { $_ !~ /^(VERSION|N|FN)$/ } | 
| 512 | 22 |  |  |  |  | 26 | sort map { uc $_ } keys %{ $self->{nodes} }; | 
|  | 141 |  |  |  |  | 207 |  | 
|  | 22 |  |  |  |  | 73 |  | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | # 'perldoc perlport' says using \r\n is wrong and confusing for a few | 
| 516 |  |  |  |  |  |  | # reasons but mainly because the value of \n is different on different | 
| 517 |  |  |  |  |  |  | # operating systems.  It recommends \x0D\x0A instead. | 
| 518 | 22 |  |  |  |  | 39 | my $newline = "\x0D\x0A"; | 
| 519 | 22 |  |  |  |  | 22 | my $begin   = 'BEGIN:VCARD'; | 
| 520 | 22 |  |  |  |  | 18 | my $end     = 'END:VCARD'; | 
| 521 |  |  |  |  |  |  |  | 
| 522 | 22 |  |  |  |  | 27 | my @lines = ($begin); | 
| 523 | 22 |  |  |  |  | 29 | for my $k (@k) { | 
| 524 | 155 |  |  |  |  | 2695 | my $nodes = $self->get($k); | 
| 525 | 155 |  |  |  |  | 204 | push @lines, map { $_->as_string() } @$nodes; | 
|  | 175 |  |  |  |  | 863 |  | 
| 526 |  |  |  |  |  |  | } | 
| 527 | 22 |  |  |  |  | 563 | return join $newline, @lines, $end, ''; | 
| 528 |  |  |  |  |  |  | } | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | sub _sort_prefs { | 
| 531 | 12 |  |  | 12 |  | 9 | my $check = shift; | 
| 532 | 12 | 100 |  |  |  | 15 | if ( $check->is_type('pref') ) { | 
| 533 | 10 |  |  |  |  | 13 | return 1; | 
| 534 |  |  |  |  |  |  | } else { | 
| 535 | 2 |  |  |  |  | 4 | return 0; | 
| 536 |  |  |  |  |  |  | } | 
| 537 |  |  |  |  |  |  | } | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | # Private method for adding nodes | 
| 540 |  |  |  |  |  |  | sub _add_node { | 
| 541 | 267 |  |  | 267 |  | 219 | my ( $self, $conf ) = @_; | 
| 542 |  |  |  |  |  |  |  | 
| 543 | 267 |  |  |  |  | 333 | my $value_fields = $self->get_lookup(); | 
| 544 |  |  |  |  |  |  |  | 
| 545 | 267 |  |  |  |  | 330 | my $node_type = uc( $conf->{node_type} ); | 
| 546 |  |  |  |  |  |  | $node_type = $node_aliases{$node_type} | 
| 547 | 267 | 100 |  |  |  | 442 | if defined $node_aliases{$node_type}; | 
| 548 |  |  |  |  |  |  |  | 
| 549 | 267 |  |  |  |  | 176 | my $field_list; | 
| 550 |  |  |  |  |  |  |  | 
| 551 | 267 | 100 |  |  |  | 357 | if ( defined $value_fields->{$node_type} ) { | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | # We know what the field list is | 
| 554 | 65 |  |  |  |  | 73 | $field_list = $value_fields->{$node_type}; | 
| 555 |  |  |  |  |  |  | } else { | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | # No defined fields - use just the 'value' one | 
| 558 | 202 |  |  |  |  | 181 | $field_list = \@default_field; | 
| 559 |  |  |  |  |  |  | } | 
| 560 | 267 | 100 |  |  |  | 406 | unless ( defined $self->{nodes}->{$node_type} ) { | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | # create space to hold list of node objects | 
| 563 | 248 |  |  |  |  | 174 | my @node_list_space; | 
| 564 | 248 |  |  |  |  | 331 | $self->{nodes}->{$node_type} = \@node_list_space; | 
| 565 |  |  |  |  |  |  | } | 
| 566 | 267 |  |  |  |  | 187 | my $last_node; | 
| 567 | 267 |  |  |  |  | 169 | foreach my $node_data ( @{ $conf->{data} } ) { | 
|  | 267 |  |  |  |  | 344 |  | 
| 568 |  |  |  |  |  |  | my $node_obj = Text::vCard::Node->new( | 
| 569 |  |  |  |  |  |  | {   node_type    => $node_type, | 
| 570 |  |  |  |  |  |  | fields       => $field_list, | 
| 571 |  |  |  |  |  |  | data         => $node_data, | 
| 572 |  |  |  |  |  |  | group        => $conf->{group} || '', | 
| 573 |  |  |  |  |  |  | encoding_out => $self->{encoding_out}, | 
| 574 |  |  |  |  |  |  | } | 
| 575 | 310 |  | 100 |  |  | 1576 | ); | 
| 576 |  |  |  |  |  |  |  | 
| 577 | 310 |  |  |  |  | 409 | push( @{ $self->{nodes}->{$node_type} }, $node_obj ); | 
|  | 310 |  |  |  |  | 488 |  | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | # store the last node so we can return it. | 
| 580 | 310 |  |  |  |  | 364 | $last_node = $node_obj; | 
| 581 |  |  |  |  |  |  | } | 
| 582 | 267 |  |  |  |  | 427 | return $last_node; | 
| 583 |  |  |  |  |  |  | } | 
| 584 |  |  |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | =head1 AUTHOR | 
| 586 |  |  |  |  |  |  |  | 
| 587 |  |  |  |  |  |  | Leo Lapworth, LLAP@cuckoo.org | 
| 588 |  |  |  |  |  |  | Eric Johnson (kablamo), github ~!at!~ iijo dot org | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | =head1 Repository (git) | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | http://github.com/ranguard/text-vcard, git://github.com/ranguard/text-vcard.git | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | Copyright (c) 2005-2010 Leo Lapworth. All rights reserved. | 
| 597 |  |  |  |  |  |  | This program is free software; you can redistribute | 
| 598 |  |  |  |  |  |  | it and/or modify it under the same terms as Perl itself. | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | L, L, | 
| 603 |  |  |  |  |  |  | L L, L L, | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | =cut | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | 1; |