File Coverage

blib/lib/Mac/Tie/PList.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             #==============================================================================a#
2              
3             package Mac::Tie::PList;
4             our $VERSION = '0.03';
5              
6             #==============================================================================a#
7              
8             =head1 NAME
9              
10             Mac::Tie::PList - Parse Apple NSDictionary objects (e.g. Preference Lists)
11            
12              
13             =head1 SYNOPSIS
14              
15             use Mac::Tie::PList;
16              
17             my $plist = Mac::Tie::PList->new_from_file("/Library/Preferences/.GlobalPreferences.plist");
18              
19             while ((my $key,$val) = each %$plist) {
20             print "$key => $val\n";
21             }
22              
23              
24             =head1 DESCRIPTION
25              
26             This module allows you to parse NSDictionary objects, as used in PList files, as tied perl
27             objects. It uses the L perl/objective-c bridge and so both xml1 and binary1
28             formats are currently supported.
29              
30             The objects are mapped as follows:
31              
32             NSNumber NSBoolean NSString => perl tied scalar
33             NSArray => perl tied array
34             NSDictionary => perl tied hash
35             NSDate => perl tied string - returns seconds since 1970
36             NSData => *WARNING* The returned sting format is not decided yet
37            
38             NOTE: Currently the module only provided read access to the data. Write access is
39             planned in the future.
40              
41             =over 4
42              
43             =cut
44              
45             #==============================================================================a#
46              
47 3     3   103865 use strict;
  3         7  
  3         111  
48 3     3   16 use warnings;
  3         7  
  3         86  
49 3     3   16 use Carp;
  3         9  
  3         329  
50 3     3   1281 use Foundation;
  0            
  0            
51             use File::Temp qw(tempfile);
52              
53             =item my $hash_ref = Mac::Tie::PList->new($data)
54              
55             Parses data and creates a new tied hash based on the data provided as a string.
56              
57             =cut
58              
59             sub new {
60             my ($obj,$xml) = @_;
61             my ($tmp_fh,$tmp_file) = tempfile();
62             $tmp_fh->print($xml);
63             $tmp_fh->close();
64             my $plist = $obj->new_from_file($tmp_file);
65             unlink($tmp_file);
66             return $plist;
67             }
68              
69              
70             =item my $hash_ref = Mac::Tie::PList->new_from_file($filename)
71              
72             Parses data and creates a new tied hash based on the contents of a file.
73              
74             =cut
75              
76            
77             sub new_from_file {
78             my ($obj,$file) = @_;
79             my $plist_obj = NSDictionary->dictionaryWithContentsOfFile_($file);
80             if ($plist_obj && $$plist_obj) {
81             tie my %plist, 'Mac::Tie::PList::Hash', $plist_obj;
82             return \%plist;
83             } else {
84             return;
85             }
86             }
87              
88              
89             sub _tie_plist {
90             my ($plist_obj) = @_;
91             if ($plist_obj->isKindOfClass_(NSArray->class) ) {
92             tie my @plist, 'Mac::Tie::PList::Array', $plist_obj;
93             return \@plist;
94             } elsif (
95             ($plist_obj->isKindOfClass_(NSCFNumber->class)) ||
96             ($plist_obj->isKindOfClass_(NSCFBoolean->class)) ||
97             ($plist_obj->isKindOfClass_(NSCFData->class)) ||
98             ($plist_obj->isKindOfClass_(NSDate->class)) ||
99             ($plist_obj->isKindOfClass_(NSCFString->class))
100             ) {
101             tie my $plist, 'Mac::Tie::PList::Scalar', $plist_obj;
102             return $plist;
103             } elsif ($plist_obj->isKindOfClass_(NSDictionary->class) ) {
104             tie my %plist, 'Mac::Tie::PList::Hash', $plist_obj;
105             return \%plist;
106             } else {
107             carp "Unknown type: $plist_obj\n";
108             return;
109             }
110             }
111              
112              
113             #==============================================================================a#
114              
115             package Mac::Tie::PList::Hash;
116              
117             use strict;
118             use warnings;
119             use Carp;
120             use Foundation;
121             use Tie::Hash;
122             use base qw(Tie::Hash);
123              
124             sub TIEHASH {
125             my ($class,$plist_obj) = @_;
126             return bless {plist_obj=>$plist_obj, hash=>{} }, $class;
127             }
128              
129             sub FETCH {
130             my ($obj,$key) = @_;
131             if ($obj->{hash}->{$key}) {
132             return $obj->{hash}->{$key};
133             } else {
134             if ($obj->{plist_obj} && ${$obj->{plist_obj}}) {
135             my $sub_obj = $obj->{plist_obj}->objectForKey_($key);
136             if ($sub_obj && $$sub_obj ) {
137             return $obj->{hash}->{$key} = Mac::Tie::PList::_tie_plist($sub_obj);
138             } else {
139             return;
140             }
141             }
142             }
143             }
144              
145             sub FIRSTKEY {
146             my ($obj) = @_;
147             my @keys;
148             if ($obj->{plist_obj} && ${$obj->{plist_obj}}) {
149             my $keys_array = $obj->{plist_obj}->allKeys;
150             for (my $i=0; $i<$keys_array->count; $i++) {
151             my $key_obj = $keys_array->objectAtIndex_($i);
152             if ($key_obj && $$key_obj) {
153             push @keys,$key_obj->description->UTF8String;
154             }
155             }
156             }
157              
158             $obj->{keys} = \@keys;
159             return $obj->NEXTKEY;
160             }
161              
162             sub NEXTKEY {
163             my ($obj) = @_;
164             return shift @{$obj->{keys}};
165             }
166              
167              
168             #==============================================================================#
169              
170             package Mac::Tie::PList::Scalar;
171              
172             use strict;
173             use warnings;
174             use Carp;
175             use Foundation;
176             use Tie::Scalar;
177             use base qw(Tie::Scalar);
178              
179             sub TIESCALAR {
180             my ($class,$plist_obj) = @_;
181             return bless {plist_obj => $plist_obj}, $class;
182             }
183              
184             sub FETCH {
185             my ($obj) = @_;
186              
187             if ($obj->{plist_obj} && ${$obj->{plist_obj}}) {
188             if ($obj->{plist_obj}->isKindOfClass_(NSDate->class) ) {
189             return $obj->{plist_obj}->timeIntervalSince1970;
190             # TODO Data is in what format? Perl should return a packed string
191             #} elsif ($obj->{plist_obj}->isKindOfClass_(NSData->class) ) {
192             # return pack "c*", $obj->{plist_obj}->bytes;
193             } else {
194             return $obj->{plist_obj}->description->UTF8String;
195             }
196             } else {
197             return;
198             }
199             }
200              
201              
202             #==============================================================================#
203              
204             package Mac::Tie::PList::Array;
205              
206             use strict;
207             use warnings;
208             use Carp;
209             use Foundation;
210             use Tie::Array;
211             use base qw(Tie::Array);
212              
213             sub TIEARRAY {
214             my ($class,$plist_obj) = @_;
215             return bless {plist_obj=>$plist_obj, array=>[]}, $class;
216             }
217              
218             sub FETCH {
219             my ($obj,$n) = @_;
220              
221             if ($obj->{array}->[$n]) {
222             return $obj->{array}->[$n];
223             } else {
224             my $sub_obj = $obj->{plist_obj}->objectAtIndex_($n);
225             if ($sub_obj && $$sub_obj ) {
226             return $obj->{array}->[$n] = Mac::Tie::PList::_tie_plist($sub_obj);
227             } else {
228             return;
229             }
230             }
231             }
232              
233             sub FETCHSIZE {
234             my ($obj) = @_;
235              
236             if ($obj->{plist_obj} && ${$obj->{plist_obj}}) {
237             return $obj->{plist_obj}->count;
238             } else {
239             return;
240             }
241             }
242              
243              
244             #==============================================================================#
245              
246             =back
247              
248             =head1 SEE ALSO
249              
250             This module is based on code from the following O'Reilly article:
251              
252             http://www.macdevcenter.com/pub/a/mac/2005/07/29/plist.html
253              
254             The Objective C Bridge is descibed at:
255              
256             http://developer.apple.com/documentation/Darwin/Reference/ManPages/man3/PerlObjCBridge.3pm.html
257              
258             Further details of NSDictionary's is available here:
259              
260             http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_cla
261             ssic/Classes/NSDictionary.html
262              
263             =head1 AUTHOR
264              
265             Gavin Brock, Egbrock@cpan.orgE
266              
267             =head1 COPYRIGHT AND LICENSE
268              
269             Copyright (C) 2006 by Gavin Brock
270              
271             This library is free software; you can redistribute it and/or modify
272             it under the same terms as Perl itself, either Perl version 5.8.0 or,
273             at your option, any later version of Perl 5 you may have available.
274              
275             =cut
276              
277              
278             #
279             # That's all folks..
280             #==============================================================================#
281              
282             1;