File Coverage

blib/lib/XML/AppleConfigProfile/Payload/Tie/Root.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # This is the code for XML::AppleConfigProfile::Payload::Tie::Root.
2             # For Copyright, please see the bottom of the file.
3              
4             package XML::AppleConfigProfile::Payload::Tie::Root;
5              
6 1     1   14 use 5.14.4;
  1         5  
  1         46  
7 1     1   6 use strict;
  1         3  
  1         38  
8 1     1   6 use warnings FATAL => 'all';
  1         2  
  1         56  
9              
10             our $VERSION = '0.00_001';
11              
12 1     1   6 use Tie::Hash; # Also gives us Tie::StdHash
  1         2  
  1         20  
13 1     1   586 use XML::AppleConfigProfile::Payload::Tie::Array;
  0            
  0            
14             use XML::AppleConfigProfile::Payload::Types qw($ProfileArray $ProfileDict $ProfileClass);
15              
16              
17             =head1 NAME
18              
19             XML::AppleConfigProfile::Payload::Tie::Root - Tying class for payload storage.
20              
21             =head1 DESCRIPTION
22              
23             This class is used to store the payload keys, and their values, for each of the
24             payload classes under C.
25              
26             In the configuration profile XML, each payload is represented by a series of
27             keys and their values. This matches up fairly well with a Perl hash, so that
28             is the mechanism that was chosen for actually getting (and messing with) the
29             data in a payload class!
30              
31             This class is used directly only by L,
32             and acts as storage for the payload keys. Subclasses are involved indirectly,
33             by providing their own list of payload keys, either replacing or supplementing
34             the list from C.
35              
36             =cut
37              
38             =head2 "CLASS" METHODS
39              
40             =head2 tie %hash, 'XML::AppleConfigProfile::Payload::Tie::Root', $self
41              
42             This method is not useful in client code, but it is documented for future
43             developers of this software.
44              
45             When this class is used to tie a hash, C will be called, with the class
46             name as the first argument. The second argument is expected to be a reference
47             to the object that will be containing this tied hash. The containing object
48             needs to implement two methods:
49              
50             =over 4
51              
52             =item _validate($key, $value)
53              
54             C<_validate> needs to return, if the value is valid, the de-tainted value. If
55             the value is not valid, then C must be returned.
56              
57             =item keys()
58              
59             C needs to return a reference to the hash of payload keys, as defined in
60             L. No attempts will be made to modify
61             the hash, so it can (and should) be read-only.
62              
63             =back
64              
65             Since the second argument is a reference pointing back to the object which
66             contains us, we are introducing a circular reference. We take responsibility
67             for "weakening" the reference provided to us.
68              
69             =cut
70              
71             sub TIEHASH {
72             my ($class, $object_ref) = @_;
73            
74             # $object_ref points to our containing object. In other words, $object_ref,
75             # if de-referenced, would give us our instance of this class.
76             # Using $object_ref around like this does, I believe, create a circular
77             # reference, which we need to break.
78             Scalar::Util::weaken($object_ref);
79            
80             # Construct our object. We need a hash for the payload, and we'll also
81             # bring along the reference to our containing instance.
82             # Our class name is made-up, to keep clients from doing weird stuff.
83             return bless {
84             payload => {},
85             object => $object_ref,
86             }, "$class";
87             }
88              
89              
90             =head2 FETCH
91              
92             Works as one would expect with a Perl hash. Either the value is returned, or
93             C is returned. Exactly I you get depends on the payload class and
94             the key you are accessing. For more details, check the payload class
95             documentation, as well as L.
96              
97             =cut
98              
99             sub FETCH {
100             my ($self, $key) = @_;
101            
102             # Our EXISTS check returns true if the key is a valid payload key name.
103             # Therefore, we need to do our own exists check, and possible return undef.
104             if (exists $self->{payload}->{$key}) {
105             return $self->{payload}->{$key};
106             }
107            
108             # At this point, our key doesn't exist right now, but we need to check for
109             # some complex types.
110             my $key_info = $self->{object}->keys()->{$key};
111             my $type = $key_info->{type};
112            
113             # If the key is an array, set up a new Array tie
114             # Exception: If the subtype is a class, then use construct()
115             if ($type == $ProfileArray) {
116             my $subtype = $key_info->{subtype};
117            
118             if ($subtype == $ProfileClass) {
119             my $object = $self->{object}->construct($key);
120             $self->{payload}->{$key} = $object;
121             }
122             else {
123             tie my @array, 'XML::AppleConfigProfile::Payload::Tie::Array', $subtype;
124             $self->{payload}->{$key} = \@array;
125             }
126            
127             return $self->{payload}->{$key};
128             }
129            
130             # If the key is a dictionary, set up a new Hash tie
131             # Exception: If the subtype is a class, then use construct()
132             # elsif ($type == $ProfileDict) {
133             # my $subtype = $key_info->{subtype};
134             # if ($subtype == $ProfileClass) {
135             # my $object = $self->{object}->construct($key);
136             # $self->{payload}->{$key} = $object;
137             # }
138             # else {
139             # tie my @array, 'XML::AppleConfigProfile::Payload::Tie::Hash', $subtype;
140             # $self->{payload}->{$key} = \@array;
141             # }
142             #
143             # return $self->{payload}->{$key};
144             # }
145            
146             # If the key is a class, instantiate it, add it to the payload, and return
147             elsif ($type == $ProfileClass) {
148             my $object = $self->{object}->construct($key);
149             $self->{payload}->{$key} = $object;
150             return $object;
151             }
152            
153             # The catch-all: The key doesn't exist, and isn't special, so return undef.
154             else {
155             ## no critic (ProhibitExplicitReturnUndef)
156             return undef;
157             ## use critic
158             }
159             }
160              
161              
162             =head2 STORE
163              
164             Works I as one would expect with a Perl hash. When setting a value to
165             a key, two checks are performed:
166              
167             =over 4
168              
169             =item *
170              
171             The key must be a valid payload key name for this payload class.
172              
173             =item *
174              
175             The value must be a valid value for the given payload key.
176              
177             =back
178              
179             Exactly what validation is performed depends first on the type of value (be it
180             a string, a boolean, data, etc.), and next on any special validation performed
181             by the payload class itself. For more details, check the payload class
182             documentation, as well as L.
183              
184             If the validation fails, the program dies.
185              
186             =cut
187              
188             sub STORE {
189             my ($self, $key, $value) = @_;
190            
191             # If we are setting to undef, then just drop the key.
192             # if (!defined $value) {
193             # $self->DELETE($key);
194             # return;
195             # }
196            
197             # Check if the proposed value is valid, and store if it is.
198             # (Validating also de-taints the value, if it's valid)
199             $value = $self->{object}->validate_key($key, $value);
200             if (defined($value)) {
201             $self->{payload}->{$key} = $value;
202             }
203             else {
204             die('Invalid value for key');
205             }
206             }
207              
208              
209             =head3 delete
210              
211             Deleting a key works as one would expect with a Perl hash. Once deleted,
212             unless a new value is set, attempts to access the key will return C.
213              
214             =cut
215              
216             sub DELETE {
217             my ($self, $key) = @_;
218             delete $self->{payload}->{$key};
219             }
220              
221              
222             =head3 clear
223              
224             Clearing a hash works as one would expect with a Perl hash. Unless new values
225             are set, attempts to access keys will return C.
226              
227             =cut
228              
229             sub CLEAR {
230             my ($self) = @_;
231             # The CLEAR method implemented in Tie::Hash uses calls to $self
232             # (specifically, calls to FIRSTKEY, NEXTKEY, and DELETE), so let's just
233             # call that code instead of reimplementing it!
234             Tie::Hash::CLEAR($self);
235             }
236              
237              
238             =head3 exists
239              
240             The operation of C is a little different from what is normally expected.
241              
242             C will return true iff the key provided is a valid payload key for this
243             payload class.
244              
245             To check if a payload key actually has a value, use C. Of course, you
246             should continue to use C if you do not know if a payload has a
247             particular key.
248              
249             =cut
250              
251             sub EXISTS {
252             my ($self, $key) = @_;
253             return 1 if exists($self->{object}->keys()->{$key});
254             return 0;
255             }
256              
257              
258             =head2 keys
259              
260             C returns a list of keys I.
261              
262             To get the a list of all keys that exist for this payload class, don't look
263             at the payload. Instead, use C on the hash returned by C.
264              
265             =head2 each
266              
267             C returns the key/value pairs I.
268              
269             =cut
270              
271             sub FIRSTKEY {
272             my ($self) = @_;
273             # We can use the code from Tie::StdHash::FIRSTKEY, instead of rewriting it.
274             return Tie::StdHash::FIRSTKEY($self->{payload});
275             }
276              
277              
278             sub NEXTKEY {
279             my ($self, $previous) = @_;
280             # We can use the code from Tie::StdHash::NEXTKEY, instead of rewriting it.
281             return Tie::StdHash::NEXTKEY($self->{payload});
282             }
283              
284              
285             =head2 scalar
286              
287             C returns the number of payload keys that have values set.
288              
289             To get the total number of keys that exist for this payload class, don't look
290             at the payload. Instead, use C on the hash returned by C.
291              
292             =cut
293              
294             sub SCALAR {
295             my ($self) = @_;
296             return scalar %{$self->{payload}};
297             }
298              
299              
300             =head1 ACKNOWLEDGEMENTS
301              
302             Refer to the L for acknowledgements.
303              
304             =head1 AUTHOR
305              
306             A. Karl Kornel, C<< >>
307              
308             =head1 COPYRIGHT AND LICENSE
309              
310             Copyright © 2014 A. Karl Kornel.
311              
312             This program is free software; you can redistribute it and/or modify it
313             under the terms of either: the GNU General Public License as published
314             by the Free Software Foundation; or the Artistic License.
315              
316             See L for more information.
317              
318             =cut
319              
320             1;