File Coverage

blib/lib/Data/Plist.pm
Criterion Covered Total %
statement 34 90 37.7
branch 11 58 18.9
condition 1 18 5.5
subroutine 9 14 64.2
pod 8 8 100.0
total 63 188 33.5


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Data::Plist - object representing a property list
4              
5             =head1 SYNOPSIS
6              
7             # Create a new plist containing $data
8             my $plist = Data::Plist->new($data);
9              
10             # Get nested arrays containing the perl data structure's
11             # information
12             my $ret = $plist->raw_data;
13              
14             # Get perl data structure
15             $ret = $plist->data;
16              
17             # Get an Objective C object
18             $ret = $plist->object;
19              
20              
21             =head1 DESCRIPTION
22              
23             Plists are intermediate structures from which nested array
24             structures of the format specified in L,
25             perl data structures and Objective C data structures can be
26             obtained.
27              
28             =cut
29              
30             =head1 SERIALIZED DATA
31              
32             Perl data structures that have been serialized become
33             nested array structures containing their data and their
34             data type. Example:
35              
36             [ array => [ string => "kitten" ], [ integer => 42], [ real => 3.14159 ] ]
37              
38             Array references are passed around when dealing with
39             serialized data.
40              
41             =head1 KEYED ARCHIVES
42              
43             Apple uses binary property lists as a serialization format from
44             Objective C, in a format C. L has the
45             capability to recognize property lists which were generated using
46             C, and to construct perl objects based on the
47             information in the property list. Objects thus created are blessed
48             under the C namespace. Thus, the root
49             ancestor of all Objective C objects thus imported is
50             L.
51              
52             =cut
53              
54             package Data::Plist;
55              
56 4     4   23550 use strict;
  4         9  
  4         148  
57 4     4   22 use warnings;
  4         7  
  4         111  
58              
59 4     4   7518 use DateTime;
  4         615818  
  4         142  
60 4     4   3077 use UNIVERSAL::require;
  4         12646  
  4         41  
61              
62 4     4   106 use vars qw/$VERSION/;
  4         9  
  4         4059  
63             $VERSION = "0.1";
64              
65             =head1 METHODS
66              
67             =head2 new
68              
69             Creates a new Data::Plist object. Generally, you will not need to
70             call this directly, as Plists are generally created by
71             L classes, and are not needed in serialization
72             when using L classes.
73              
74             =cut
75              
76             sub new {
77 35     35 1 46 my $class = shift;
78 35         299 return bless { data => undef, @_ } => $class;
79             }
80              
81             =head2 collapse $data
82              
83             Takes an array of serialized data C<$data>. Recursively
84             returns the actual data, without the datatype labels.
85              
86             =cut
87              
88             sub collapse {
89 323     323 1 287 my $self = shift;
90 323         278 my ($data) = @_;
91              
92 323 50       525 unless ( ref $data eq "ARRAY" ) {
93 0         0 warn "Got $data?";
94 0         0 return "???";
95             }
96              
97 323 100 33     1209 if ( $data->[0] eq "array" ) {
    100          
    100          
    100          
    50          
98 4         5 return [ map $self->collapse($_), @{ $data->[1] } ];
  4         16  
99             } elsif ( $data->[0] eq "dict" ) {
100 3         4 my %dict = %{ $data->[1] };
  3         13  
101 3         17 $dict{$_} = $self->collapse( $dict{$_} ) for keys %dict;
102 3         19 return \%dict;
103             } elsif ( $data->[0] eq "string" ) {
104 7 50       40 return $data->[1] eq '$null' ? undef : $data->[1];
105             } elsif ( $data->[0] eq "date" ) {
106 1         11 return DateTime->from_epoch( epoch => $data->[1] + 978307200 );
107             } elsif ( $data->[0] eq "UID" and ref $data->[1] ) {
108 0         0 return $self->collapse( $data->[1] );
109             } else {
110 308         669 return $data->[1];
111             }
112              
113             }
114              
115             =head2 raw_data
116              
117             Returns the plist as a set of nested arrays of the format specified in
118             L.
119              
120             =cut
121              
122             sub raw_data {
123 34     34 1 8833 my $self = shift;
124 34         149 return $self->{data};
125             }
126              
127             =head2 data
128              
129             Returns the plist as its corresponding perl data structure.
130              
131             =cut
132              
133             sub data {
134 16     16 1 8310 my $self = shift;
135 16         44 return $self->collapse( $self->raw_data );
136             }
137              
138             =head2 is_archive
139              
140             Checks if the plist is actually an archived Objective C generated by
141             C. Returns true if it is. See L.
142              
143             =cut
144              
145             sub is_archive {
146 0     0 1   my $self = shift;
147 0           my $data = $self->raw_data;
148 0 0         return unless $data->[0] eq "dict";
149              
150 0 0         return unless exists $data->[1]{'$archiver'};
151 0 0         return unless $data->[1]{'$archiver'}[0] eq "string";
152 0 0         return unless $data->[1]{'$archiver'}[1] eq "NSKeyedArchiver";
153              
154 0 0         return unless exists $data->[1]{'$objects'};
155 0 0         return unless $data->[1]{'$objects'}[0] eq "array";
156              
157 0 0         return unless exists $data->[1]{'$top'};
158              
159 0 0         return unless exists $data->[1]{'$version'};
160 0 0         return unless $data->[1]{'$version'}[0] eq "integer";
161 0 0         return unless $data->[1]{'$version'}[1] eq "100000";
162              
163 0           return 1;
164             }
165              
166             =head2 unref
167              
168             Recursively strips references from the plist.
169              
170             =cut
171              
172             sub unref {
173 0     0 1   my $self = shift;
174 0           my $p = shift;
175 0 0 0       if ( $p->[0] eq "UID" ) {
    0 0        
    0          
    0          
176             return [
177 0           "UID",
178             $self->unref( $self->raw_data->[1]{'$objects'}[1][ $p->[1] ] )
179             ];
180             } elsif ( $p->[0] eq "array" ) {
181 0           return [ "array", [ map { $self->unref($_) } @{ $p->[1] } ] ];
  0            
  0            
182             } elsif ( $p->[0] eq "dict" ) {
183 0           my %dict = %{ $p->[1] };
  0            
184 0           $dict{$_} = $self->unref( $dict{$_} ) for keys %dict;
185 0           return [ "dict", \%dict ];
186             } elsif ( $p->[0] eq "data"
187             and ref $p->[1]
188             and $p->[1]->isa("Data::Plist") )
189             {
190 0           return $p->[1]->_raw_object;
191             } else {
192 0           return $p;
193             }
194             }
195              
196             =head2 reify $data
197              
198             Takes serialized data (see L) C<$data>
199             and checks if it's a keyed archive (see L
200             DATA>). If the data is a keyed archive, it blesses it into
201             the appropriate perl class.
202              
203             =cut
204              
205             sub reify {
206 0     0 1   my $self = shift;
207 0           my ( $data ) = @_;
208              
209 0 0         return $data unless ref $data;
210 0 0         if ( ref $data eq "HASH" ) {
    0          
211 0           my $hash = { %{$data} };
  0            
212 0           my $class = delete $hash->{'$class'};
213 0           $hash->{$_} = $self->reify( $hash->{$_} ) for keys %{$hash};
  0            
214 0 0 0       if ( $class
      0        
      0        
215             and ref $class
216             and ref $class eq "HASH"
217             and $class->{'$classname'} )
218             {
219 0           my $classname = "Data::Plist::Foundation::" . $class->{'$classname'};
220 0 0         if ( not $classname->require ) {
    0          
221 0           warn "Can't require $classname: $@\n";
222             } elsif ( not $classname->isa( "Data::Plist::Foundation::NSObject" ) ) {
223 0           warn "$classname isn't a Data::Plist::Foundation::NSObject\n";
224             } else {
225 0           bless( $hash, $classname );
226 0           $hash = $hash->replacement;
227             }
228             }
229 0           return $hash;
230             } elsif ( ref $data eq "ARRAY" ) {
231 0           return [ map $self->reify( $_ ), @{$data} ];
  0            
232             } else {
233 0           return $data;
234             }
235             }
236              
237             sub _raw_object {
238              
239 0     0     my $self = shift;
240 0 0         return unless $self->is_archive;
241 0           return $self->unref( $self->raw_data->[1]{'$top'}[1]{root} );
242             }
243              
244             =head2 object
245              
246             If the plist is an Objective C object archive created with
247             C (see L), returns the object
248             blessed into the corresponding class under
249             L. Otherwise, returns undef.
250              
251             =cut
252              
253             sub object {
254 0     0 1   my $self = shift;
255              
256 0           require Data::Plist::Foundation::NSObject;
257              
258 0 0         return unless $self->is_archive;
259 0           return $self->reify( $self->collapse( $self->_raw_object ) );
260             }
261              
262             =head1 DEPENDENCIES
263              
264             L, L, L, L,
265             L, L, L, L,
266             L
267              
268             =head1 BUGS AND LIMITATIONS
269              
270             No XML reader is included at current.
271              
272             Please report any bugs or feature requests to
273             C, or through the web interface at
274             L.
275              
276             =head1 AUTHORS
277              
278             Alex Vandiver and Jacky Chang.
279              
280             Based on plutil.pl, written by Pete Wilson
281              
282             =head1 LICENSE
283              
284             This module is free software; you can redistribute it and/or modify it
285             under the same terms as Perl itself. See L.
286              
287             =cut
288              
289             1;