| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #!/usr/bin/perl -w | 
| 2 |  |  |  |  |  |  | # -*- Mode: perl -*- | 
| 3 |  |  |  |  |  |  | #====================================================================== | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | # This package is free software and is provided "as is" without | 
| 6 |  |  |  |  |  |  | # express or implied warranty.  It may be used, redistributed and/or | 
| 7 |  |  |  |  |  |  | # modified under the same terms as perl itself. ( Either the Artistic | 
| 8 |  |  |  |  |  |  | # License or the GPL. ) | 
| 9 |  |  |  |  |  |  | # | 
| 10 |  |  |  |  |  |  | # $Id: Component.pm,v 1.48 2001/08/04 04:59:36 srl Exp $ | 
| 11 |  |  |  |  |  |  | # | 
| 12 |  |  |  |  |  |  | # (C) COPYRIGHT 2000-2001, Reefknot developers. | 
| 13 |  |  |  |  |  |  | # | 
| 14 |  |  |  |  |  |  | # See the AUTHORS file included in the distribution for a full list. | 
| 15 |  |  |  |  |  |  | #====================================================================== | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | =head1 NAME | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | Net::ICal::Component -- the base class for ICalender components | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | =cut | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | package Net::ICal::Component; | 
| 24 | 1 |  |  | 1 |  | 6 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 29 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 1 |  |  | 1 |  | 952 | use UNIVERSAL; | 
|  | 1 |  |  |  |  | 13 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 27 | 1 |  |  | 1 |  | 27 | use base qw(Class::MethodMapper); | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 880 |  | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 1 |  |  | 1 |  | 52659 | use Net::ICal::Util qw(add_validation_error); | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 3864 |  | 
| 30 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | You never create an instance of this class directly, so we'll assume | 
| 33 |  |  |  |  |  |  | $c is an already created component. | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | # returns an ICal string for this component. | 
| 36 |  |  |  |  |  |  | $c->as_ical; | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | This is the base class we derive specific ICal components from. | 
| 41 |  |  |  |  |  |  | It contains a map of properties which can be set and accessed at will; | 
| 42 |  |  |  |  |  |  | see the docs for Class::MethodMapper for more on how it works. | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =begin testing | 
| 45 |  |  |  |  |  |  | use lib "./lib"; | 
| 46 |  |  |  |  |  |  | use Net::ICal; | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | $comp = new Net::ICal::Alarm( | 
| 49 |  |  |  |  |  |  | action => 'DISPLAY', | 
| 50 |  |  |  |  |  |  | trigger => "20000101T073000", | 
| 51 |  |  |  |  |  |  | description => "Wake Up!" | 
| 52 |  |  |  |  |  |  | ); | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | =end testing | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =head1 CONSTRUCTORS | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | =head2 new($name, $map, %args) | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | Creates a new ICal component of type C<$name>, with Class::MethodMapper | 
| 61 |  |  |  |  |  |  | map C<$map> and arguments C<%args>. You never call this directly, but | 
| 62 |  |  |  |  |  |  | you use the specific component's new constructor instead, which in turn | 
| 63 |  |  |  |  |  |  | calls this. | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | =begin testing | 
| 66 |  |  |  |  |  |  | TODO: { | 
| 67 |  |  |  |  |  |  | local $TODO = "write tests for the new method, please"; | 
| 68 |  |  |  |  |  |  | ok(0, "need tests here"); | 
| 69 |  |  |  |  |  |  | }; | 
| 70 |  |  |  |  |  |  | =end testing | 
| 71 |  |  |  |  |  |  | =cut | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | sub _param_set { | 
| 74 |  |  |  |  |  |  | #TODO: allow things like $foo->description ("blah blah", altrep => 'foo'); | 
| 75 | 0 |  |  | 0 |  | 0 | my ($self, $key, $val) = @_; | 
| 76 | 0 |  |  |  |  | 0 | my ($class) = $self =~ /^(.*?)=/g; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 0 |  |  |  |  | 0 | my @params = @{$self->get_meta ('options', $key)}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 79 | 0 | 0 |  |  |  | 0 | if (ref($val) eq 'HASH') { | 
| 80 | 0 |  |  |  |  | 0 | foreach my $param (keys %$val) { | 
| 81 | 0 | 0 |  |  |  | 0 | unless (grep { $_ eq lc($param) } ('content', @params)) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 82 | 0 |  |  |  |  | 0 | warn "${class}->$key has no $param parameter. skipping.\n"; | 
| 83 | 0 |  |  |  |  | 0 | delete $val->{$param}; | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  | } | 
| 86 | 0 |  |  |  |  | 0 | $self->{$key}->{value} = $val; | 
| 87 |  |  |  |  |  |  | } else { | 
| 88 | 0 |  |  |  |  | 0 | $self->{$key}->{value} = { content => $val }; | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | sub new { | 
| 93 | 44 |  |  | 44 | 1 | 92 | my ($classname, $name, $map, %args) = @_; | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | #TODO: WTF is a type 'volatile' and why are we using it? | 
| 96 |  |  |  |  |  |  | #       The Class::MethodMapper docs say that "Generally, a | 
| 97 |  |  |  |  |  |  | #       `parameter' is something that can be saved and restored, | 
| 98 |  |  |  |  |  |  | #       whereas a `volatile' is not serialized at save-time." | 
| 99 |  |  |  |  |  |  | #       Can someone clarify this? --srl | 
| 100 |  |  |  |  |  |  | #BUG: 424107 | 
| 101 | 44 |  |  |  |  | 218 | $map->{'type'} = { | 
| 102 |  |  |  |  |  |  | type => 'volatile', | 
| 103 |  |  |  |  |  |  | doc => 'type of the component', | 
| 104 |  |  |  |  |  |  | value => $name | 
| 105 |  |  |  |  |  |  | }; | 
| 106 |  |  |  |  |  |  | # So we can keep a list of validation errors for Net::ITIP | 
| 107 | 44 |  |  |  |  | 209 | $map->{'errlog'} = { | 
| 108 |  |  |  |  |  |  | type => 'volatile', # we don't want to see this in serialized data | 
| 109 |  |  |  |  |  |  | doc => 'list of (ITIP) validation errors', | 
| 110 |  |  |  |  |  |  | domain => 'ref', | 
| 111 |  |  |  |  |  |  | options => 'ARRAY', | 
| 112 |  |  |  |  |  |  | value => [], | 
| 113 |  |  |  |  |  |  | }; | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | # FIXME: handle X-properties here. | 
| 117 |  |  |  |  |  |  | # BUG: 411196 | 
| 118 |  |  |  |  |  |  |  | 
| 119 | 44 |  |  |  |  | 163 | my $self = new Class::MethodMapper; | 
| 120 | 44 |  |  |  |  | 408 | bless $self, $classname; | 
| 121 | 44 |  |  |  |  | 357 | $self->set_map (%$map); | 
| 122 | 44 |  |  |  |  | 2286 | $self->set (%args); | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 44 |  |  |  |  | 257 | return $self; | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | =head2 new_from_ical($icaldata) | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | Creates a new Net::ICal::Component from a string containing iCalendar | 
| 131 |  |  |  |  |  |  | data.  Use this to read in a new object before you do things with it. | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | Returns a Net::ICal::Component object on success, or undef on failure. | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | =cut | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | sub new_from_ical { | 
| 138 | 17 |  |  | 17 | 1 | 20091 | my ($class, $ical) = @_; | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | # put the string into something for the callback function below to use | 
| 141 | 17 |  |  |  |  | 279 | my @lines = split (/\015?\012/, $ical);		# portability | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | #FIXME: this should return undef if the ical is invalid | 
| 144 |  |  |  |  |  |  | #BUG: 424109 | 
| 145 | 17 |  |  |  |  | 67 | return _parse_lines (\@lines); | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | =pod | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =head1 METHODS | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =head2 type ([$string]) | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | Get or set the type of this component. You aren't supposed to ever | 
| 155 |  |  |  |  |  |  | set this directly. To create a component of a specific type, use | 
| 156 |  |  |  |  |  |  | the new method of the corresponding class. | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | =head2 validate | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | Returns 1 if the component is valid according to RFC 2445. If it isn't | 
| 162 |  |  |  |  |  |  | undef is returned, and $@ contains a listref of errors | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | =cut | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub validate { | 
| 167 | 44 |  |  | 44 | 1 | 61 | my ($self) = @_; | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 44 | 100 |  |  |  | 52 | if (@{$self->errlog}) { | 
|  | 44 |  |  |  |  | 185 |  | 
| 170 | 7 |  |  |  |  | 216 | $@ = $self->errlog; | 
| 171 | 7 |  |  |  |  | 193 | return undef; | 
| 172 |  |  |  |  |  |  | } else { | 
| 173 | 37 |  |  |  |  | 953 | return 1; | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | =head2 as_ical | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | Returns an ICal string that represents this component | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =begin testing | 
| 182 |  |  |  |  |  |  | TODO: { | 
| 183 |  |  |  |  |  |  | local $TODO = 'write tests for as_ical'; | 
| 184 |  |  |  |  |  |  | ok(0, "need tests here"); | 
| 185 |  |  |  |  |  |  | }; | 
| 186 |  |  |  |  |  |  | =end testing | 
| 187 |  |  |  |  |  |  | =cut | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | sub as_ical { | 
| 190 | 0 |  |  | 0 | 1 | 0 | my ($self) = @_; | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | # make the BEGIN: VALARM line, or whatever | 
| 193 | 0 |  |  |  |  | 0 | my $ical = "BEGIN:" . $self->type . "\015\012"; | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # this is a callback that Class::MethodMapper will use | 
| 196 |  |  |  |  |  |  | # to generate the ical text. | 
| 197 |  |  |  |  |  |  | my $cb = sub { | 
| 198 | 0 |  |  | 0 |  | 0 | my ($self, $key, $value) = @_; | 
| 199 | 0 |  |  |  |  | 0 | my $line; | 
| 200 |  |  |  |  |  |  |  | 
| 201 | 0 |  |  |  |  | 0 | $key =~ s/_/-/g; | 
| 202 | 0 |  |  |  |  | 0 | $key = uc ($key); | 
| 203 |  |  |  |  |  |  |  | 
| 204 | 0 | 0 |  |  |  | 0 | return unless $value->{value}; | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | # if this object is just a reference to something, look at that object. | 
| 207 | 0 | 0 |  |  |  | 0 | if (not defined $value->{domain}) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 208 | 0 |  |  |  |  | 0 | $line .= $key . ":" . $value->{value} . "\015\012"; | 
| 209 |  |  |  |  |  |  | } elsif ($value->{domain} eq 'ref') { | 
| 210 | 0 | 0 |  |  |  | 0 | if ($value->{options} eq 'ARRAY') { | 
|  |  | 0 |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | # for every line in this array, if it's a ref, call the | 
| 212 |  |  |  |  |  |  | # referenced object's as_ical method; otherwise output a | 
| 213 |  |  |  |  |  |  | # key:value pair. | 
| 214 | 0 |  |  |  |  | 0 | foreach my $val (@{$value->{value}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 215 | 0 | 0 |  |  |  | 0 | if (ref ($val)) { | 
| 216 | 0 | 0 |  |  |  | 0 | if (UNIVERSAL::isa ($val, 'Net::ICal::Property')) { | 
| 217 | 0 |  |  |  |  | 0 | $line .= $key . $val->as_ical . "\015\012"; | 
| 218 |  |  |  |  |  |  | } else { | 
| 219 | 0 |  |  |  |  | 0 | $line .= $val->as_ical(); | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  | } else { | 
| 222 | 0 |  |  |  |  | 0 | $line .= $key . ":$val\015\012"; | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  | } elsif ($value->{options} eq 'HASH') { | 
| 226 |  |  |  |  |  |  | } else { | 
| 227 |  |  |  |  |  |  | # assume it's a class, and call its as_ical method | 
| 228 | 0 |  |  |  |  | 0 | $line .= $key . $value->{value}->as_ical . "\015\012"; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | # if this is a thing without its own subclass, it's a hashref. | 
| 232 |  |  |  |  |  |  | # output the key value (DESCRIPTION, for example) and then | 
| 233 |  |  |  |  |  |  | # the hash's keys and values like ";key=value". | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | } elsif ($value->{domain} eq 'param') { | 
| 236 | 0 |  |  |  |  | 0 | my $xhash = $value->{value}; | 
| 237 | 0 |  |  |  |  | 0 | $line = $key; | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | # the 'content' key is the name of this property. | 
| 240 | 0 |  |  |  |  | 0 | foreach my $xkey (keys %$xhash) { | 
| 241 | 0 | 0 |  |  |  | 0 | next if ($xkey eq 'content'); | 
| 242 | 0 |  |  |  |  | 0 | $line .= ';' . uc ($xkey) . "=" . $xhash->{$xkey}; | 
| 243 |  |  |  |  |  |  | } | 
| 244 | 0 |  |  |  |  | 0 | $line .= ":" . $xhash->{content} . "\015\012"; | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | # otherwise just output a key-value pair. | 
| 247 |  |  |  |  |  |  | } else { | 
| 248 | 0 |  |  |  |  | 0 | $line .= $key . ":" . $value->{value} . "\015\012"; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 0 |  |  |  |  | 0 | $ical .= $line; | 
| 251 | 0 |  |  |  |  | 0 | }; | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | # call the Class::MethodMapper callback. | 
| 254 | 0 |  |  |  |  | 0 | $self->save ('parameter', $cb); | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | # OUTPUT END:VALARM or whatever. | 
| 257 | 0 |  |  |  |  | 0 | $ical .= "END:" . $self->type . "\015\012"; | 
| 258 | 0 |  |  |  |  | 0 | return $ical; | 
| 259 |  |  |  |  |  |  | } | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | =head2 has_one_of (@propertynames) | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | returns a true value if one of the listed property names is present | 
| 264 |  |  |  |  |  |  | on the component and undef if not | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | =for testing | 
| 267 |  |  |  |  |  |  | ok($comp->has_one_of ('action', 'attendee'), "we have action, so pass"); | 
| 268 |  |  |  |  |  |  | ok(not($comp->has_one_of ('summary', 'attendee')), "we have neither summary nor attendee so fail"); | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | =cut | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | sub has_one_of { | 
| 273 | 0 |  |  | 0 | 1 | 0 | my ($self, @props) = @_; | 
| 274 |  |  |  |  |  |  |  | 
| 275 | 0 |  |  |  |  | 0 | foreach my $prop (@props) { | 
| 276 | 0 | 0 |  |  |  | 0 | return 1 if defined ($self->get ($prop)); | 
| 277 |  |  |  |  |  |  | } | 
| 278 | 0 |  |  |  |  | 0 | return undef; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | =head2 has_required_property (name, [value]) | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | checks whether the component has a value for property 'name' and | 
| 284 |  |  |  |  |  |  | optionally checks whether it is value 'value' | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | =for testing | 
| 287 |  |  |  |  |  |  | ok($comp->has_required_property ('action'), "we have action, so pass"); | 
| 288 |  |  |  |  |  |  | ok(not($comp->has_required_property ('summary')), "we don't have summary so fail"); | 
| 289 |  |  |  |  |  |  | ok($comp->has_required_property ('action','DISPLAY'), "action contains 'DISPLAY', so pass"); | 
| 290 |  |  |  |  |  |  | ok(not($comp->has_required_property ('action','nonsense')), "action doesn't contain 'nonsense', so fail"); | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | =cut | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | sub has_required_property { | 
| 295 | 0 |  |  | 0 | 1 | 0 | my ($self, $property, $value) = @_; | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 0 | 0 |  |  |  | 0 | do { | 
| 298 | 0 |  |  |  |  | 0 | $@ = $self->type . " needs a " . | 
| 299 |  |  |  |  |  |  | $property . " property for this method"; | 
| 300 | 0 |  |  |  |  | 0 | return undef; | 
| 301 |  |  |  |  |  |  | } unless (defined $self->get ($property)); | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 0 | 0 |  |  |  | 0 | if (defined $value) { | 
| 304 | 0 | 0 |  |  |  | 0 | do { | 
| 305 | 0 |  |  |  |  | 0 | $@ = "$property needs to be set to $value for this method"; | 
| 306 | 0 |  |  |  |  | 0 | return undef; | 
| 307 |  |  |  |  |  |  | } unless ($self->get ($property) eq $value); | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  |  | 
| 310 | 0 |  |  |  |  | 0 | return 1; | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | =head2 has_illegal_property (name) | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | checks whether the component has a value for property 'name' and | 
| 316 |  |  |  |  |  |  | returns a true value if it has, and undef if it doesn't | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | =for testing | 
| 319 |  |  |  |  |  |  | ok($comp->has_illegal_property ('action'), "we have action, so fail"); | 
| 320 |  |  |  |  |  |  | ok(not($comp->has_illegal_property ('attendee')), "we don't have attendee so pass"); | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | =cut | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | sub has_illegal_property { | 
| 325 | 0 |  |  | 0 | 1 | 0 | my ($self, $property) = @_; | 
| 326 |  |  |  |  |  |  |  | 
| 327 | 0 | 0 |  |  |  | 0 | do { | 
| 328 | 0 |  |  |  |  | 0 | $@ = "$property not allowed for this method"; | 
| 329 | 0 |  |  |  |  | 0 | return 1; | 
| 330 |  |  |  |  |  |  | } if defined ($self->get ($property)); | 
| 331 |  |  |  |  |  |  |  | 
| 332 | 0 |  |  |  |  | 0 | return undef; | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | =head2 has_only_one_of (name1, name2) | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | returns undef if both the properties name1 and name2 are present. Otherwise, | 
| 338 |  |  |  |  |  |  | it returns a true value. On error, it sets $@. | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | =for testing | 
| 341 |  |  |  |  |  |  | ok($comp->has_only_one_of ('action', 'summary'), "we have action, and not summary, so pass"); | 
| 342 |  |  |  |  |  |  | ok(not($comp->has_only_one_of ('action', 'trigger')), "we have both action and trigger, so fail"); | 
| 343 |  |  |  |  |  |  | ok($comp->as_only_one_of ('foo', 'bar'), "we have neither, so pass"); | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | =cut | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | sub has_only_one_of { | 
| 348 | 0 |  |  | 0 | 1 | 0 | my ($self, $prop1, $prop2) = @_; | 
| 349 |  |  |  |  |  |  |  | 
| 350 | 0 |  |  |  |  | 0 | my $val1 = $self->get ($prop1); | 
| 351 | 0 |  |  |  |  | 0 | my $val2 = $self->get ($prop2); | 
| 352 | 0 | 0 | 0 |  |  | 0 | do { | 
| 353 | 0 |  |  |  |  | 0 | $@ = "Properties $prop1 and $prop2 are mutually exclusive for this method"; | 
| 354 | 0 |  |  |  |  | 0 | return undef; | 
| 355 |  |  |  |  |  |  | } if (defined ($val1) and defined ($val2)); | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | #return (defined ($val1) or defined ($val2)); | 
| 358 | 0 |  |  |  |  | 0 | return 1; | 
| 359 |  |  |  |  |  |  | } | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | =pod | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =head1 INTERNAL METHODS | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | These are for internal use only, and are included here for the benefit | 
| 366 |  |  |  |  |  |  | of Net::ICal developers. | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | =head2 _identify_component($line) | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | the first line of the iCal will look like BEGIN:VALARM or something. | 
| 372 |  |  |  |  |  |  | we need to know what comes after the V, because that's what | 
| 373 |  |  |  |  |  |  | sort of component we'll be creating. | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | Returns ALARM, EVENT, TODO, JOURNAL, FREEBUSY, etc, or undef for | 
| 376 |  |  |  |  |  |  | failure. | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | =for testing | 
| 379 |  |  |  |  |  |  | ok(&Net::ICal::Component::_identify_component("BEGIN:VTODO") eq "TODO", "Identify TODO component"); | 
| 380 |  |  |  |  |  |  | ok(Net::ICal::Component::_identify_component("BeGiN:vToDo") eq "TODO", "Identify mixed case component"); | 
| 381 |  |  |  |  |  |  | ok(not(Net::ICal::Component::_identify_component("BEGIN:xyzzy")), "can't identify nonsense component"); | 
| 382 |  |  |  |  |  |  | ok(not(Net::ICal::Component::_identify_component("")), "can't identify component in empty string"); | 
| 383 |  |  |  |  |  |  | ok(not(Net::ICal::Component::_identify_component()), "can't identify component in undef"); | 
| 384 |  |  |  |  |  |  | ok(not(Net::ICal::Component::_identify_component(123)), "can't identify component in number"); | 
| 385 |  |  |  |  |  |  | =cut | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | sub _identify_component { | 
| 388 | 71 |  |  | 71 |  | 94 | my ($line) = @_; | 
| 389 |  |  |  |  |  |  |  | 
| 390 | 71 |  |  |  |  | 395 | my ($bogus, $comp) = $line =~ /^BEGIN:(V)?(\w+)$/gi; | 
| 391 |  |  |  |  |  |  |  | 
| 392 | 71 |  | 50 |  |  | 296 | return uc($comp) || undef; | 
| 393 |  |  |  |  |  |  | } | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | =pod | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | =head2 _create_component($comp) | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | $comp is "ALARM" or something. We generate the name of a type of object | 
| 400 |  |  |  |  |  |  | we want to create, and call the _create method on that object. | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | =for testing | 
| 403 |  |  |  |  |  |  | ok(Net::ICal::Event::_create_component("TODO"), "Create TODO component"); | 
| 404 |  |  |  |  |  |  | ok(not(Net::ICal::Event::_create_component("xyzzy")), "Can't create nonsense component"); | 
| 405 |  |  |  |  |  |  | ok(not(Net::ICal::Event::_create_component("")), "Can't create component from empty string"); | 
| 406 |  |  |  |  |  |  | ok(not(Net::ICal::Event::_create_component()), "Can't create component from undef"); | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | =cut | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | sub _create_component { | 
| 411 | 44 |  |  | 44 |  | 58 | my ($comp) = @_; | 
| 412 |  |  |  |  |  |  |  | 
| 413 | 44 |  |  |  |  | 94 | $comp = "Net::ICal::" . ucfirst (lc ($comp)); | 
| 414 | 44 |  |  |  |  | 2492 | eval "require $comp"; | 
| 415 | 44 | 50 |  |  |  | 163 | if ($@) { | 
| 416 | 0 |  |  |  |  | 0 | $@ = "Unknown component $comp"; | 
| 417 | 0 |  |  |  |  | 0 | return undef; | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  |  | 
| 420 | 44 |  |  |  |  | 226 | return $comp->_create; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | =pod | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =head2 _unfold(@lines) | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | Handle multiline fields; see "unfolding" in RFC 2445.  Make all the | 
| 429 |  |  |  |  |  |  | multiple fields we've been handed into single-line fields. | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | =for testing | 
| 432 |  |  |  |  |  |  | my $unfoldlines = []; | 
| 433 |  |  |  |  |  |  | ok(Net::ICal::Event::_unfold($unfoldlines), "Unfold valid iCal lines"); | 
| 434 |  |  |  |  |  |  | ok(not(Net::ICal::Event::_unfold("x\ny\nz\n")), "Can't unfold invalid iCal lines"); | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | =cut | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | sub _unfold { | 
| 439 | 141 |  |  | 141 |  | 181 | my ($lines) = @_; | 
| 440 |  |  |  |  |  |  |  | 
| 441 | 141 |  |  |  |  | 192 | my $line = shift @$lines; | 
| 442 | 141 |  | 66 |  |  | 758 | while (@$lines and $lines->[0] =~ /^ /) { | 
| 443 | 0 |  |  |  |  | 0 | chomp $line; | 
| 444 | 0 |  |  |  |  | 0 | $line .= substr (shift @$lines, 1); | 
| 445 |  |  |  |  |  |  | } | 
| 446 | 141 |  |  |  |  | 294 | return $line; | 
| 447 |  |  |  |  |  |  | } | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | =pod | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | =head2 _fold($line) | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | =cut | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | sub _fold { | 
| 456 | 0 |  |  | 0 |  | 0 | my ($line) = @_; | 
| 457 | 0 |  |  |  |  | 0 | my $folded; | 
| 458 |  |  |  |  |  |  |  | 
| 459 | 0 |  |  |  |  | 0 | while (length $line > 76) { | 
| 460 |  |  |  |  |  |  | # don't break lines in the middle of words | 
| 461 | 0 |  |  |  |  | 0 | $line =~ s/(.{1,76}\W)//; | 
| 462 |  |  |  |  |  |  | # when we wrap a line, use this as a newline | 
| 463 | 0 |  |  |  |  | 0 | $folded .= $1 . "\015\012 "; | 
| 464 |  |  |  |  |  |  | } | 
| 465 | 0 |  |  |  |  | 0 | return $folded; | 
| 466 |  |  |  |  |  |  | } | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | =pod | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | =head2 _parse_lines(\@lines) | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | Parse and validate the lines of iCalendar data we got to make sure it | 
| 473 |  |  |  |  |  |  | looks iCal-like. | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | =cut | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | sub _parse_lines { | 
| 478 | 44 |  |  | 44 |  | 62 | my ($lines) = @_; | 
| 479 |  |  |  |  |  |  |  | 
| 480 | 44 |  |  |  |  | 96 | my $comp = _identify_component(shift @$lines); | 
| 481 | 44 | 50 |  |  |  | 98 | unless ($comp) { | 
| 482 | 0 |  |  |  |  | 0 | warn "Not a valid ical stream\n"; | 
| 483 | 0 |  |  |  |  | 0 | return undef; | 
| 484 |  |  |  |  |  |  | } | 
| 485 |  |  |  |  |  |  |  | 
| 486 | 44 |  |  |  |  | 86 | my $self = _create_component($comp); | 
| 487 | 44 | 50 |  |  |  | 119 | unless ($self) { | 
| 488 | 0 |  |  |  |  | 0 | while (shift @$lines) { | 
| 489 | 0 | 0 |  |  |  | 0 | last if /^END/; | 
| 490 |  |  |  |  |  |  | } | 
| 491 | 0 |  |  |  |  | 0 | return undef; | 
| 492 |  |  |  |  |  |  | } | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | # give a callback for Class::MethodMaker to call when it | 
| 495 |  |  |  |  |  |  | # restores the data from @lines. | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | my $cb = sub { | 
| 498 | 141 | 50 |  | 141 |  | 3561 | return undef unless @$lines; | 
| 499 |  |  |  |  |  |  |  | 
| 500 | 141 |  |  |  |  | 266 | my $line = _unfold($lines); | 
| 501 |  |  |  |  |  |  |  | 
| 502 | 141 | 100 |  |  |  | 537 | if ($line =~ /^BEGIN:/) { | 
|  |  | 100 |  |  |  |  |  | 
| 503 | 27 |  |  |  |  | 53 | unshift (@$lines, $line); | 
| 504 | 27 |  |  |  |  | 58 | my $foo = _parse_lines ($lines); | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | # Calendar.pm has alarms/todos/etc methods, so add the s | 
| 507 | 27 |  |  |  |  | 7989 | my $name = lc (_identify_component ($line)) . 's'; | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | # see if there's already an existing list | 
| 510 | 27 |  | 66 |  |  | 98 | my $ref = $self->get ($name) || (); | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | # move to parse errors from child components to our log | 
| 513 | 27 | 100 |  |  |  | 467 | if ($foo) { | 
| 514 | 20 |  |  |  |  | 23 | push (@{$self->errlog}, @{$foo->errlog}); | 
|  | 20 |  |  |  |  | 103 |  | 
|  | 20 |  |  |  |  | 506 |  | 
| 515 | 20 |  |  |  |  | 449 | push (@$ref, $foo); | 
| 516 |  |  |  |  |  |  | } else { | 
| 517 | 7 | 50 |  |  |  | 17 | if (ref ($@)) { | 
| 518 | 7 |  |  |  |  | 96 | push (@{$self->errlog}, @{$@}); | 
|  | 7 |  |  |  |  | 38 |  | 
|  | 7 |  |  |  |  | 178 |  | 
| 519 |  |  |  |  |  |  | } else { | 
| 520 | 0 |  |  |  |  | 0 | add_validation_error ($self, $@); | 
| 521 |  |  |  |  |  |  | } | 
| 522 |  |  |  |  |  |  | } | 
| 523 | 27 |  |  |  |  | 103 | return ($name, $ref); | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | } elsif ($line =~ /^END:(\w+)$/) { | 
| 526 | 44 |  |  |  |  | 128 | return undef; | 
| 527 |  |  |  |  |  |  | } else { | 
| 528 |  |  |  |  |  |  | # parse out the iCalendar lines. | 
| 529 | 70 |  |  |  |  | 134 | my ($key, $value)      = _parse_property($line); | 
| 530 | 70 |  |  |  |  | 149 | my ($class, $paramstr) = _parse_parameter($key); | 
| 531 |  |  |  |  |  |  |  | 
| 532 | 70 |  |  |  |  | 127 | $class = lc ($class); | 
| 533 |  |  |  |  |  |  | # make sure we have a valid function name | 
| 534 | 70 |  |  |  |  | 116 | $class =~ s/-/_/g; | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | # FIXME: handle X-properties here. | 
| 537 |  |  |  |  |  |  | # BUG: 411196 | 
| 538 |  |  |  |  |  |  |  | 
| 539 | 70 | 100 |  |  |  | 296 | if (not defined $self->get_meta ('type', $class)) { | 
| 540 | 2 |  |  |  |  | 24 | add_validation_error ($self, "There is no $class method"); | 
| 541 | 2 |  |  |  |  | 49 | return ('type', $self->get_meta ('value', 'type')); | 
| 542 |  |  |  |  |  |  | } | 
| 543 |  |  |  |  |  |  | # avoid warnings for doing eq with undef below | 
| 544 |  |  |  |  |  |  | # no domain means simple string/integer, so only | 
| 545 |  |  |  |  |  |  | # one of them is allowed | 
| 546 | 68 | 100 |  |  |  | 872 | if (not defined $self->get_meta ('domain', $class)) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 547 | 30 |  |  |  |  | 322 | my $old = $self->get_meta ('value', lc($key)); | 
| 548 | 30 | 50 |  |  |  | 258 | if ($old) { | 
| 549 | 0 |  |  |  |  | 0 | add_validation_error ($self, "Only one $key allowed; skipping"); | 
| 550 | 0 |  |  |  |  | 0 | return ($class, $old); | 
| 551 |  |  |  |  |  |  | } | 
| 552 | 30 |  |  |  |  | 114 | return ($class, $value); | 
| 553 |  |  |  |  |  |  | # we either have an array of values, or a class for the | 
| 554 |  |  |  |  |  |  | # property | 
| 555 |  |  |  |  |  |  | } elsif ($self->get_meta ('domain', $class) eq 'ref') { | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | # set up the array to refer to. It may be an array of objects | 
| 558 |  |  |  |  |  |  | # or just an array of values; _load_property will do either. | 
| 559 | 38 | 100 |  |  |  | 760 | if ($self->get_meta ('options', $class) eq 'ARRAY') { | 
| 560 |  |  |  |  |  |  | # the array elements can be refs too | 
| 561 | 6 |  |  |  |  | 59 | my $prop = _load_property ($class, $value, $line); | 
| 562 | 6 | 50 |  |  |  | 17 | unless (defined $prop) { | 
| 563 | 0 |  |  |  |  | 0 | add_validation_error ($self, "Error loading property $key"); | 
| 564 |  |  |  |  |  |  | } | 
| 565 | 6 |  |  |  |  | 22 | my $val = $self->get_meta ('value', $class); | 
| 566 | 6 | 100 |  |  |  | 59 | if (defined $val) { | 
| 567 | 3 |  |  |  |  | 5 | push (@$val, $prop); | 
| 568 | 3 |  |  |  |  | 12 | return ($class, $val); | 
| 569 |  |  |  |  |  |  | } else { | 
| 570 | 3 |  |  |  |  | 16 | return ($class, [$prop]); | 
| 571 |  |  |  |  |  |  | } | 
| 572 |  |  |  |  |  |  | } else { | 
| 573 |  |  |  |  |  |  | # if this thing we're looking at needs to be made a | 
| 574 |  |  |  |  |  |  | # Net::ICal::subclass object, load that module and call that | 
| 575 |  |  |  |  |  |  | # subclass's new_from_ical method on this line of ical text. | 
| 576 | 32 |  |  |  |  | 331 | my $prop = _load_property ($self->get_meta ('options', $class), | 
| 577 |  |  |  |  |  |  | $value, $line); | 
| 578 | 32 | 50 |  |  |  | 3868 | unless (defined $prop) { | 
| 579 | 0 |  |  |  |  | 0 | add_validation_error ($self, "Error loading property $key"); | 
| 580 |  |  |  |  |  |  | } | 
| 581 | 32 |  |  |  |  | 135 | return ($class, $prop); | 
| 582 |  |  |  |  |  |  | } | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | # if there are parameters for this thing, but not an actual subclass, | 
| 585 |  |  |  |  |  |  | # build a hash and return a reference to it. See, for example, | 
| 586 |  |  |  |  |  |  | # DESCRIPTION fields, which can have an ALTREP (like a URL) or a | 
| 587 |  |  |  |  |  |  | # LANGUAGE. We don't need a separate class for it; a hash will suffice. | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | } elsif ($self->get_meta ('domain', $class) eq 'param') { | 
| 590 | 0 | 0 |  |  |  | 0 | my @params = $paramstr ? split (/;/, $paramstr) : (); | 
| 591 | 0 |  |  |  |  | 0 | my %foo = (content => $value); | 
| 592 |  |  |  |  |  |  |  | 
| 593 | 0 |  |  |  |  | 0 | foreach my $keyvalue (@params) { | 
| 594 | 0 |  |  |  |  | 0 | my ($pkey, $pvalue) = split (/=/, $keyvalue); | 
| 595 | 0 |  |  |  |  | 0 | $foo{$pkey} = $pvalue; | 
| 596 |  |  |  |  |  |  | } | 
| 597 | 0 |  |  |  |  | 0 | return ($class, \%foo); | 
| 598 |  |  |  |  |  |  | } | 
| 599 |  |  |  |  |  |  | } | 
| 600 | 44 |  |  |  |  | 289 | }; | 
| 601 | 44 |  |  |  |  | 180 | $self->restore($cb); | 
| 602 |  |  |  |  |  |  |  | 
| 603 | 44 |  |  |  |  | 175 | my $warnings; | 
| 604 | 44 | 100 |  |  |  | 49 | if (@{$self->errlog}) { | 
|  | 44 |  |  |  |  | 269 |  | 
| 605 |  |  |  |  |  |  | # save parse errors | 
| 606 | 8 |  |  |  |  | 222 | $warnings = $self->errlog; | 
| 607 |  |  |  |  |  |  | # empty the errlog, since parse errors don't have to be fatal | 
| 608 | 8 |  |  |  |  | 208 | $self->errlog ([]); | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  |  | 
| 611 | 44 | 100 |  |  |  | 1325 | if ($self->validate) { | 
| 612 |  |  |  |  |  |  | # if we passed, put back the parse errors, which apparently | 
| 613 |  |  |  |  |  |  | # really were non-fatal | 
| 614 | 37 | 100 |  |  |  | 103 | $self->errlog ($warnings) if (defined $warnings); | 
| 615 | 37 |  |  |  |  | 1019 | return $self; | 
| 616 |  |  |  |  |  |  | } else { | 
| 617 |  |  |  |  |  |  | # oops, we didn't validate. Might be because of those parse | 
| 618 |  |  |  |  |  |  | # errors. put those at the start. | 
| 619 | 7 | 50 |  |  |  | 20 | unshift (@{$@}, @$warnings) if (defined $warnings); | 
|  | 0 |  |  |  |  | 0 |  | 
| 620 | 7 |  |  |  |  | 165 | return undef; | 
| 621 |  |  |  |  |  |  | } | 
| 622 |  |  |  |  |  |  | } | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | =pod | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | =head2 _parse_property($property) | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | Given a property line from an iCalendar file, parses it and returns the | 
| 629 |  |  |  |  |  |  | name and the value of that property. | 
| 630 |  |  |  |  |  |  |  | 
| 631 |  |  |  |  |  |  | =for testing | 
| 632 |  |  |  |  |  |  | ok(0, "need tests here"); | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | =cut | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  |  | 
| 637 |  |  |  |  |  |  | #FIXME: these will break if there's a : in a parameter value.  We're also | 
| 638 |  |  |  |  |  |  | #       not handling FOO:value1,value2 properly. | 
| 639 |  |  |  |  |  |  | #BUG: 233739 | 
| 640 |  |  |  |  |  |  | sub _parse_property { | 
| 641 | 70 |  |  | 70 |  | 95 | my ($prop) = @_; | 
| 642 |  |  |  |  |  |  |  | 
| 643 | 70 |  |  |  |  | 368 | my ($name, $value) = $prop =~ /^(.*?):(.*)$/g; | 
| 644 |  |  |  |  |  |  |  | 
| 645 | 70 |  |  |  |  | 183 | return ($name, $value); | 
| 646 |  |  |  |  |  |  | } | 
| 647 |  |  |  |  |  |  |  | 
| 648 |  |  |  |  |  |  | =pod | 
| 649 |  |  |  |  |  |  |  | 
| 650 |  |  |  |  |  |  | =head2 _parse_parameter($propname) | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | Given a property name/key section, parses it and returns the param name and | 
| 653 |  |  |  |  |  |  | the parameter string. | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | =for testing | 
| 656 |  |  |  |  |  |  | ok(0, "need tests here"); | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | =cut | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | sub _parse_parameter { | 
| 661 | 70 |  |  | 70 |  | 103 | my ($propname) = @_; | 
| 662 |  |  |  |  |  |  |  | 
| 663 | 70 |  |  |  |  | 386 | my ($paramname, $paramstr) = $propname =~ /^(.*?)(?:;(.*)|$)/g; | 
| 664 | 70 |  |  |  |  | 182 | return ($paramname, $paramstr); | 
| 665 |  |  |  |  |  |  | } | 
| 666 |  |  |  |  |  |  |  | 
| 667 |  |  |  |  |  |  | =pod | 
| 668 |  |  |  |  |  |  |  | 
| 669 |  |  |  |  |  |  | =head2 _load_property($class, $value, $line) | 
| 670 |  |  |  |  |  |  |  | 
| 671 |  |  |  |  |  |  | If a new ICal subclass object needs to be created, load the module | 
| 672 |  |  |  |  |  |  | and return a new instance of it. Otherwise, just return the value | 
| 673 |  |  |  |  |  |  | of the property. | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | =for testing | 
| 676 |  |  |  |  |  |  | ok(0, "need tests here"); | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | =cut | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | sub _load_property { | 
| 681 | 38 |  |  | 38 |  | 347 | my ($class, $value, $line) = @_; | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | #FIXME: How do we want to handle this?  Do we really want | 
| 684 |  |  |  |  |  |  | #       separate packages for Rrule and Exrule, and subclass them? | 
| 685 | 38 |  |  |  |  | 182 | $class =~ s/\b(?:rrule|exrule)$/recurrence/i; | 
| 686 | 38 | 100 |  |  |  | 118 | unless ($class =~ /::/) { | 
| 687 | 6 |  |  |  |  | 16 | $class = "Net::ICal::" . ucfirst (lc ($class)); | 
| 688 |  |  |  |  |  |  | } | 
| 689 | 38 |  |  |  |  | 46 | my $prop; | 
| 690 | 38 |  |  |  |  | 2001 | eval "require $class"; | 
| 691 | 38 | 100 |  |  |  | 146 | unless ($@) { | 
| 692 | 32 | 50 |  |  |  | 209 | if ($class->can ('new_from_ical')) { | 
| 693 | 0 |  |  |  |  | 0 | return $class->new_from_ical($line); | 
| 694 |  |  |  |  |  |  | } else { | 
| 695 |  |  |  |  |  |  | # for things like Time, which are just a value, not a Property, | 
| 696 |  |  |  |  |  |  | # so they don't have new_from_ical | 
| 697 | 32 |  |  |  |  | 133 | return $class->new (ical => $value); | 
| 698 |  |  |  |  |  |  | } | 
| 699 |  |  |  |  |  |  | } else { | 
| 700 | 6 |  |  |  |  | 16 | return $value; | 
| 701 |  |  |  |  |  |  | } | 
| 702 |  |  |  |  |  |  | } | 
| 703 |  |  |  |  |  |  |  | 
| 704 |  |  |  |  |  |  | 1; | 
| 705 |  |  |  |  |  |  |  | 
| 706 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | =head2 Net::ICal | 
| 709 |  |  |  |  |  |  |  | 
| 710 |  |  |  |  |  |  | More documentation pointers can be found in L. | 
| 711 |  |  |  |  |  |  |  | 
| 712 |  |  |  |  |  |  | =head2 Class::MethodMapper | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | Most of the internals of this code are built on C::MM. You need to | 
| 715 |  |  |  |  |  |  | understand what it does first. | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | =cut |