File Coverage

blib/lib/XML/AppleConfigProfile/Payload/Tie/Array.pm
Criterion Covered Total %
statement 21 70 30.0
branch 0 22 0.0
condition 0 3 0.0
subroutine 7 20 35.0
pod n/a
total 28 115 24.3


line stmt bran cond sub pod time code
1             # This is the code for XML::AppleConfigProfile::Payload::Tie::Array.
2             # For Copyright, please see the bottom of the file.
3              
4             package XML::AppleConfigProfile::Payload::Tie::Array;
5              
6 1     1   10 use 5.14.4;
  1         3  
  1         31  
7 1     1   6 use strict;
  1         2  
  1         37  
8 1     1   7 use warnings FATAL => 'all';
  1         2  
  1         145  
9              
10             our $VERSION = '0.00_001';
11              
12 1     1   6 use Scalar::Util qw(blessed);
  1         2  
  1         92  
13 1     1   1330 use Tie::Array; # Also gives us Tie::StdArray
  1         1510  
  1         107  
14 1     1   707 use XML::AppleConfigProfile::Payload::Types qw(:all);
  1         3  
  1         241  
15 1     1   716 use XML::AppleConfigProfile::Payload::Types::Validation qw(:types);
  1         3  
  1         1323  
16              
17              
18             =head1 NAME
19              
20             XML::AppleConfigProfile::Payload::Tie::Array - Tying class for arrays of things.
21              
22             =head1 DESCRIPTION
23              
24             This class is used to store an array of I. Exactly what I are
25             being stored is specified at the time the tie is made.
26              
27             There are several payload types that contain arrays of things. For example,
28             the root profile has the all-important key C, which is an
29             array of payloads.
30              
31             This class is used by payload classes to represent an array.
32              
33             =cut
34              
35             =head2 "CLASS" METHODS
36              
37             =head2 tie @array, 'XML::AppleConfigProfile::Payload::Tie::Array', $value_type
38              
39             When this class is tied to an array, C will be called, with the class
40             name as the first argument.
41              
42             C<$value_type> can be a scalar or a ref. If C<$value_type> is a reference, it
43             must be blessed. We keep a record of C<$value_type>'s class, and all values
44             placed into the array must be of the same class. We let the class take
45             responsibility for validating its contents.
46              
47             If C<$value_type> is a scalar, then C<$value_type> must
48             match one of the types from L,
49             except for C<$ProfileClass>. The standard type validation function from
50             L will be used.
51              
52             If C<$value_type> is not a valid scalar or a valid reference, then an exception
53             will be thrown.
54              
55             =cut
56              
57             sub TIEARRAY {
58 0     0     my ($class, $value_type, $validator_ref) = @_;
59            
60             # This is what we'll eventually return
61 0           my %object;
62            
63             # We'll still have an array, for convenience
64 0           $object{array} = [];
65            
66             # If we have a ref, it must be a class
67 0 0         if (ref $value_type) {
68 0           my $value_class = blessed $value_type;
69 0 0         if (!defined $value_class) {
70 0           die "Attempting to pass a non-blessed ref";
71             }
72 0           $object{is_class} = 1;
73 0           $object{validator} = $value_class;
74             }
75            
76             # If we don't have a ref, then it needs to be a specific scalar
77             else {
78 0           $object{is_class} = 0;
79            
80             # Set up the appropriate validator, based on the type
81 0 0 0       if ($value_type == $ProfileString) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
82 0           $object{validator} = \&validate_string;
83             }
84             elsif ($value_type == $ProfileNumber) {
85 0           $object{validator} = \&validate_number;
86             }
87             elsif ($value_type == $ProfileReal) {
88 0           $object{validator} = \&validate_real;
89             }
90             elsif ( ($value_type == $ProfileData)
91             || ($value_type == $ProfileNSDataBlob)
92             ) {
93 0           $object{validator} = \&validate_data;
94             }
95             elsif ($value_type == $ProfileBool) {
96 0           $object{validator} = \&validate_bool;
97             }
98             elsif ($value_type == $ProfileDate) {
99 0           $object{validator} = \&validate_date;
100             }
101             elsif ($value_type == $ProfileUUID) {
102 0           $object{validator} = \&validate_uuid;
103             }
104             elsif ($value_type == $ProfileIdentifier) {
105 0           $object{validator} = \&validate_identifier;
106             }
107             else {
108 0           die "Value type is unknown";
109             }
110             } # Done checking for ref vs. scalar
111              
112 0           return bless \%object, $class;
113             }
114              
115              
116             =head2 FETCH
117              
118             Works as one would expect with a Perl array. Returns the entry at the specified
119             index. Since methods are in place to prevent storing C, as long as the
120             index is valid at the time of the call, you will get something back.
121              
122             =cut
123              
124             sub FETCH {
125 0     0     my ($self, $index) = @_;
126            
127 0           return $self->{array}->[$index];
128             }
129              
130              
131             =head2 STORE
132              
133             Storing items at a specific index is not allowed. This is to help prevent
134             C from appearing in the array. Instead, use C or C.
135              
136             =cut
137              
138             sub STORE {
139 0     0     my ($self, $index, $value) = @_;
140 0           die "Storing items at specific indexes is not allowed";
141             }
142              
143              
144             =head3 delete
145              
146             Deleting items at a specific index is not allowed. Perl has deprecated this.
147             Instead, use C, C, or C.
148              
149             =cut
150              
151             sub DELETE {
152 0     0     my ($self, $index) = @_;
153 0           die "Deleting items at specific indexes is not allowed";
154             }
155              
156              
157             =head3 scalar
158              
159             Works as expected, returning the number of items in the array.
160              
161             =cut
162              
163             sub FETCHSIZE {
164 0     0     my ($self) = @_;
165              
166 0           return scalar @{$self->{array}};
  0            
167             }
168              
169              
170             =head3 STORESIZE
171              
172             Works almost as expected. Making an array smaller will delete items off of the
173             end of the array. Making the array bigger (that is, presizing) has no effect.
174              
175             =cut
176              
177             sub STORESIZE {
178 0     0     my ($self, $count) = @_;
179            
180 0 0         return if ($count >= $self->FETCHSIZE);
181 0           $#{$self->{array}} = $count - 1;
  0            
182             }
183              
184              
185             =head3 EXTEND
186              
187             If Perl attempts to pre-extend the array, nothing happens.
188              
189             =cut
190              
191             sub EXTEND {
192 0     0     my ($self, $count) = @_;
193             }
194              
195              
196             =head2 exists
197              
198             Works as expected for a Perl array: Returns true if the specified index is
199             still valid for the array.
200              
201             =cut
202              
203             sub EXISTS {
204 0     0     my ($self, $index) = @_;
205            
206             # We can use the code from Tie::StdArray, instead of rewriting it.
207 0           return Tie::StdArray::EXISTS($self->{array}, $index);
208             }
209              
210              
211             =head2 CLEAR
212              
213             Replacing the array with an empty list works to remove all of the entries from
214             the array.
215              
216             =cut
217              
218             sub CLEAR {
219 0     0     my ($self) = @_;
220              
221 0           $self->{array} = [];
222             }
223              
224              
225             =head2 push
226              
227             Works as expected for a Perl array, with two exceptions:
228              
229             =over 4
230              
231             =item *
232              
233             C is not a valid array item.
234              
235             =item *
236              
237             If this is not an array of objects, then the value will be validated before
238             being added to the array.
239              
240             =back
241              
242             An exception will be thrown if either of the two points above fails.
243              
244             =cut
245              
246             sub PUSH {
247 0     0     my $self = CORE::shift @_;
248            
249             # Run the validation
250 0           @_ = $self->_validate(@_);
251            
252             # Let Tie::StdArray do the rest!
253 0           return Tie::StdArray::PUSH($self->{array}, @_);
254             }
255              
256              
257             =head2 pop
258              
259             Works as expected for a Perl array.
260              
261             =cut
262              
263             sub POP {
264 0     0     my ($self) = @_;
265 0           return Tie::StdArray::POP($self->{array});
266             }
267              
268              
269             =head2 shift
270              
271             Works as expected for a Perl array.
272              
273             =cut
274              
275             sub SHIFT {
276 0     0     my ($self) = @_;
277 0           return Tie::StdArray::SHIFT($self->{array});
278             }
279              
280              
281             =head2 unshift
282              
283             Works as expected for a Perl array, with two exceptions:
284              
285             =over 4
286              
287             =item *
288              
289             C is not a valid array item.
290              
291             =item *
292              
293             If this is not an array of objects, then the value will be validated before
294             being added to the array.
295              
296             =back
297              
298             An exception will be thrown if either of the two points above fails.
299             =cut
300              
301             sub UNSHIFT {
302 0     0     my $self = CORE::shift @_;
303            
304             # Run the validation
305 0           @_ = $self->_validate(@_);
306            
307             # Let Tie::StdArray do the rest!
308 0           return Tie::StdArray::UNSHIFT($self->{array}, @_);
309             }
310              
311              
312             =head2 splice
313              
314             Works as expected for a Perl array, but if you are using C to add
315             entries to the array, take note of these two exceptions:
316              
317             =over 4
318              
319             =item *
320              
321             C is not a valid array item.
322              
323             =item *
324              
325             If this is not an array of objects, then the value will be validated before
326             being added to the array.
327              
328             =back
329              
330             An exception will be thrown if either of the two points above fails.
331              
332             =cut
333              
334             sub SPLICE {
335             # We can't use Tie::Array or Tie::StdArray for this, because it expects
336             # something we can't easily give. We'll have to do it ourselves.
337             my $self = CORE::shift @_;
338            
339             # We'll need the current array size for reference
340             my $size = $self->FETCHSIZE;
341            
342             # Get the offset from the parameters, or default to 0
343             # If offset is negative, make it relative to the array end
344             my $offset = scalar @_ ? shift @_ : 0;
345             $offset += $size if $offset < 0;
346            
347             # Get the length from the parameters. If length wasn't provided, then
348             # we're grabbing all of the array starting at $offset
349             my $length = scalar @_ ? shift @_ : $size - $offset;
350            
351             # If there are any parameters left, then they are items to insert.
352             # Validate them before continuing.
353             if (scalar @_ >= 0) {
354             @_ = $self->_validate(@_);
355             }
356            
357             # Do the splice and return.
358             return splice($self->{array}, $offset , $length, @_);
359             }
360              
361              
362             =head2 _validate
363              
364             Given a list of items, each one will be validated, and the validated list will
365             be returned.
366              
367             An exception will be thrown if any of the list items is undef, or if any of
368             the list items fails validation, or if the caller is not expecting an array.
369              
370             =cut
371              
372             sub _validate {
373             my $self = CORE::shift @_;
374            
375             # If we are not returning an array, then die now
376             if (!wantarray) {
377             die "_validate expects to return an array";
378             }
379            
380             # We can't use a foreach loop, because our items might be Readonly,
381             # and the way Perl does aliasing means assigning to the foreach $item
382             # triggers a "modification of a read-only value" error.
383             my @validated_array;
384            
385             # Go through each item, making sure it is valid
386             for (my $i = 0; $i < scalar @_; $i++) {
387             my $item = $_[$i];
388            
389             # Undef is not a valid value
390             if (!defined $item) {
391             die "Adding undef items is not allowed";
392             }
393            
394             # If we are an array of objects, check the class name
395             if ($self->{is_class}) {
396             my $item_class = blessed $item;
397             if ($item_class ne $self->{validator}) {
398             die "Attempting to add item of a different class";
399             }
400             $validated_array[$i] = $item;
401             }
402            
403             # If we are not objects, then use the validation routine
404             else {
405             # Call the validation routine
406             my $validated_item = $self->{validator}->($item);
407            
408             # If $item suddenly became undef, it was invalid
409             if (!defined $validated_item) {
410             die "Attempting to insert invalid item";
411             }
412            
413             $validated_array[$i] = $validated_item;
414             } # Done checking class or not-class
415             } # Done checking each item
416            
417             return @validated_array;
418             }
419              
420              
421             =head1 ACKNOWLEDGEMENTS
422              
423             Refer to L for acknowledgements.
424              
425             =head1 AUTHOR
426              
427             A. Karl Kornel, C<< >>
428              
429             =head1 COPYRIGHT AND LICENSE
430              
431             Copyright © 2014 A. Karl Kornel.
432              
433             This program is free software; you can redistribute it and/or modify it
434             under the terms of either: the GNU General Public License as published
435             by the Free Software Foundation; or the Artistic License.
436              
437             See L for more information.
438              
439             =cut
440              
441             1;