File Coverage

lib/Class/Composite/Container.pm
Criterion Covered Total %
statement 81 95 85.2
branch 28 46 60.8
condition 7 13 53.8
subroutine 20 23 86.9
pod 17 17 100.0
total 153 194 78.8


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Class::Composite::Container - Collection of Class::Composite::Element
4              
5             =head1 SYNOPSIS
6              
7             use Class::Composite::Container;
8             my $c = Class::Composite::Container->new();
9             $c->nextElement();
10             $c->resetPointer();
11             $c->nextElement();
12             $obj = $c->getElement();
13             $list = $c->getElements(2, 5);
14              
15             =head1 DESCRIPTION
16              
17             I acts as a collection of elements.
18              
19             =head1 INHERITANCE
20              
21             Class::Composite
22              
23             =cut
24             package Class::Composite::Container;
25              
26 2     2   59054 use strict;
  2         4  
  2         64  
27 2     2   10 use warnings::register;
  2         3  
  2         271  
28 2     2   11 use Scalar::Util qw ( blessed );
  2         7  
  2         233  
29              
30 2     2   9 use base qw( Class::Composite );
  2         4  
  2         775  
31              
32             our $VERSION = 0.1;
33              
34              
35             =head1 METHODS
36              
37             =head2 init( %param )
38              
39             Parameters are 'elements' and 'elempointer'
40             init is useful for inheritance, you don't need to redefine new(), but you can redefined init().
41             See I documentation.
42              
43             =cut
44             sub init : method {
45 3     3 1 121 my $self = shift;
46 3         28 $self->SUPER::init( @_ );
47 3   50     41 $self->{elements} ||= [];
48 3   50     16 $self->{elempointer} ||= 0;
49 3         11 $self;
50             }
51              
52              
53             =head2 elements( ARRAY )
54              
55             Get/Set elements array
56              
57             =cut
58             sub elements : method {
59 1     1 1 1 my $self = shift;
60 1 50       4 my $elem = shift or return $self->{elements};
61 1 50       4 ref $elem eq 'ARRAY' or return $self->_warn("Not an array ref: $elem");
62 1         2 $self->{elements} = $elem;
63 1         4 $self;
64             }
65              
66              
67             =head2 elempointer( integer )
68              
69             Gets or sets the elements pointer. The pointer is garenteed to be between 0 and the number of elements - 1
70              
71             =cut
72             sub elempointer : method {
73 1     1 1 1 my ($self, $elem) = @_;
74 1 50       6 defined $elem or return $self->{elempointer};
75 0 0       0 $elem = $self->nOfElements - 1 if $elem >= $self->nOfElements;
76 0 0       0 $elem = 0 if $elem < 0;
77 0         0 $self->{elempointer} = int $elem;
78 0         0 $self;
79             }
80              
81              
82             =head2 addElement( @elements )
83              
84             The I method adds elements to the collection.
85             Returns the collection if ok.
86              
87             =cut
88             sub addElement : method {
89 6     6 1 1969 my ($self, @args) = @_;
90              
91 6         15 foreach (@args) {
92 9 100       44 $self->_addThis(this => $_) or return;
93             }
94 5         40 $self;
95             }
96              
97              
98             =head2 addElementFlat( @elements )
99              
100             Same than addElement, but flatten everything before adding.
101             If you add a collection, it will add all elements individually, not the collection.
102             Returns the collection if ok.
103              
104             =cut
105             sub addElementFlat : method {
106 1     1 1 5 my ($self, @args) = @_;
107 1         2 foreach (@args) {
108 1 50       4 $self->_addThis( this => $_,
109             flat => 1 ) or return;
110             }
111 1         4 $self;
112             }
113              
114              
115             #
116             # _addThis(this => $id or $object, flat => 0/1)
117             #
118             sub _addThis {
119 10     10   22 my ($self, %args) = @_;
120 10         16 my $object = $args{this};
121 10   100     45 my $flat = $args{flat} || 0;
122 10 100       28 $self->checkElement( $object ) or return $self->_warn("Element to add is not of type ".$self->elementType);
123 9 100 66     33 if ($flat and ref($object)) {
124 1 50       11 my $objects = $object->isa('Class::Composite::Container') ? $object->getLeaves
125             : [ $object ];
126 1         3 $self->_addTheseObj( $objects );
127             }
128             else {
129 8         62 $self->_addTheseObj( [$object] );
130             }
131             }
132              
133              
134             sub _addTheseObj {
135 9     9   12 my ($self, $objects) = @_;
136 9         11 push @{$self->{elements}}, @$objects;
  9         56  
137             }
138              
139              
140             =head2 checkElement( $elem )
141              
142             Returns true if $elem can be added to the container.
143             The element must be of the same type than elementType() (see I) or is undef.
144             This method is called for each element added to the collection.
145              
146             =cut
147             sub checkElement {
148 10     10 1 70 my ($self, $elem) = @_;
149 10 50       33 my $type = $self->elementType or return 1;
150 10 100       24 return 1 unless defined $elem;
151 9 100       39 if (blessed($elem)) {
152 8         49 return $elem->isa($type);
153             }
154             else {
155 1         7 return ref($elem) eq $type;
156             }
157             }
158              
159              
160             =head2 getElement( $index )
161              
162             Returns the element asked. If $index < 0 it backtracks from the last element.
163             If no index is given, returns the current element.
164              
165             =cut
166             sub getElement {
167 5     5 1 9 my ($self, $id) = @_;
168 5 100       13 $id = $self->elempointer unless defined $id;
169              
170 5 50       17 return unless exists $self->{elements};
171              
172 5 100       13 if (defined( $self->{elements}->[$id] )) {
173 4         31 return $self->{elements}->[$id];
174             } else {
175 1         5 return undef;
176             }
177             }
178              
179              
180             =head2 getElements( $start, $end )
181              
182             Returns an array ref of elements.
183             $start and $end are indexes.
184             $end is optional - if not given all elements after $start are returned.
185             If neither $start and $end are given, returns all elements.
186              
187             =cut
188             sub getElements {
189 7     7 1 10 my ($self, $start, $end) = @_;
190 7   50     27 $start ||= 0;
191 7 50       16 return [] unless $self->{elements};
192 7 50       15 $end = @{$self->{elements}} - 1 unless defined $end;
  7         11  
193 7         14 [ @{$self->{elements}}[$start .. $end] ];
  7         32  
194             }
195              
196              
197             =head2 removeElement( $index )
198              
199             If no $index is given, the current element is removed.
200             Rearrange the collection by shifting to the left the elements > $elem.
201             Returns the element removed if ok.
202              
203             =cut
204             sub removeElement {
205 1     1 1 3 my ($self, $index) = @_;
206 1 50       12 $index = $self->elempointer unless defined $index;
207 1         3 splice @{ $self->{elements} }, $index, 1;
  1         31  
208             }
209              
210              
211             =head2 removeAll()
212              
213             Reset the collection and returns the collection.
214              
215             =cut
216             sub removeAll {
217 1     1 1 3 my $self = shift;
218 1         3 $self->{elements} = [];
219 1         2 $self->{elempointer} = 0;
220 1         4 $self;
221             }
222              
223              
224             =head2 nOfElements()
225              
226             Returns the number of elements
227              
228             =cut
229             sub nOfElements {
230 7 100   7 1 25 scalar(@{$_[0]->{elements}}) || 0;
  7         52  
231             }
232              
233              
234             =head2 nextElement()
235              
236             Returns the current element and increments the internal index.
237             You can use in a while loop such as:
238              
239             while ( my $elem = $container->nextElement ) { ... }
240              
241             =cut
242             sub nextElement {
243 4     4 1 6 my $self = shift;
244 4         6 $self->getElement( $self->incrPointer() );
245             }
246              
247              
248             =head2 previousElement()
249              
250             Decrements the internal index and returns the element
251              
252             =cut
253             sub previousElement {
254 0     0 1 0 my $self = shift;
255 0 0       0 $self->decrPointer or do { $self->resetPointer(); return };
  0         0  
  0         0  
256 0         0 $self->getElement();
257             }
258              
259              
260             =head2 incrPointer()
261              
262             Increments the internal index
263              
264             =cut
265             sub incrPointer : method {
266 4     4 1 21 $_[0]->{elempointer}++;
267             }
268              
269              
270             =head2 decrPointer()
271              
272             Decrements the internal index
273              
274             =cut
275             sub decrPointer : method {
276 0     0 1 0 $_[0]->{elempointer}--;
277             }
278              
279              
280             =head2 resetPointer()
281              
282             Reset pointer to retrieve the first element again
283             Returns the pointer value before being reset
284              
285             =cut
286             sub resetPointer : method {
287 0     0 1 0 my $self = shift;
288 0         0 my $ret = $self->{elempointer};
289 0   0     0 $self->{elempointer} = shift || 0;
290 0         0 $ret;
291             }
292              
293              
294             =head2 setPointer( $index )
295              
296             Set pointer to $index. The internal index will be set to 0 if $index < 0 and will be set to the number of elements - 1 if $index >= number of elements
297             Returns the pointer's former value
298              
299             =cut
300             sub setPointer : method {
301 1     1 1 3 my ($self, $i) = @_;
302 1         2 my $current = $self->{elempointer};
303 1 50       3 $i = $self->nOfElements - 1 if ($i >= $self->nOfElements);
304 1 50       4 $i = 0 if ($i < 0);
305 1         2 $self->{elempointer} = $i;
306 1         4 $current;
307             }
308              
309              
310              
311             1;
312              
313             __END__